From 08032f1877b71f1919b5adf258f90d35b0541a96 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:33:45 +0700 Subject: [PATCH] chore(rust): update toolchain and dependencies with preserved vendor sources --- .gitattributes | 8 +- assets/licenses/usb.txt | 5 +- rust_backend/Cargo.lock | 167 +- rust_backend/Cargo.toml | 15 +- rust_backend/crates/extensions/Cargo.toml | 2 +- .../crates/providers/src/musicbrainz.rs | 2 +- .../crates/providers/src/resolver/http.rs | 4 +- rust_backend/rust-toolchain.toml | 2 +- rust_backend/vendor/README.md | 20 +- .../libusb1-sys-0.7.0/.cargo-checksum.json | 1 + .../libusb1-sys-0.7.0/.cargo_vcs_info.json | 6 + .../vendor/libusb1-sys-0.7.0/CHANGELOG.md | 54 + .../vendor/libusb1-sys-0.7.0/Cargo.lock | 37 + .../vendor/libusb1-sys-0.7.0/Cargo.toml | 64 + .../vendor/libusb1-sys-0.7.0/Cargo.toml.orig | 47 + rust_backend/vendor/libusb1-sys-0.7.0/LICENSE | 21 + .../vendor/libusb1-sys-0.7.0/README.md | 92 + .../vendor/libusb1-sys-0.7.0/build.rs | 224 + .../vendor/libusb1-sys-0.7.0/libusb/AUTHORS | 245 + .../vendor/libusb1-sys-0.7.0/libusb/COPYING | 504 ++ .../vendor/libusb1-sys-0.7.0/libusb/README | 32 + .../vendor/libusb1-sys-0.7.0/libusb/README.md | 33 + .../libusb/libusb/Makefile.am | 102 + .../libusb/libusb/Makefile.am.extra | 26 + .../libusb/libusb/Makefile.in | 1030 ++++ .../libusb1-sys-0.7.0/libusb/libusb/core.c | 3086 ++++++++++ .../libusb/libusb/descriptor.c | 1707 +++++ .../libusb1-sys-0.7.0/libusb/libusb/hotplug.c | 489 ++ .../libusb1-sys-0.7.0/libusb/libusb/io.c | 2869 +++++++++ .../libusb/libusb/libusb-1.0.def | 209 + .../libusb/libusb/libusb-1.0.rc | 53 + .../libusb1-sys-0.7.0/libusb/libusb/libusb.h | 2471 ++++++++ .../libusb1-sys-0.7.0/libusb/libusb/libusbi.h | 1599 +++++ .../libusb/libusb/os/darwin_usb.c | 3057 +++++++++ .../libusb/libusb/os/darwin_usb.h | 158 + .../libusb/libusb/os/emscripten_webusb.cpp | 896 +++ .../libusb/libusb/os/events_posix.c | 340 + .../libusb/libusb/os/events_posix.h | 62 + .../libusb/libusb/os/events_windows.c | 214 + .../libusb/libusb/os/events_windows.h | 46 + .../libusb/libusb/os/haiku_pollfs.cpp | 372 ++ .../libusb/libusb/os/haiku_usb.h | 113 + .../libusb/libusb/os/haiku_usb_backend.cpp | 532 ++ .../libusb/libusb/os/haiku_usb_raw.cpp | 235 + .../libusb/libusb/os/haiku_usb_raw.h | 188 + .../libusb/libusb/os/linux_netlink.c | 401 ++ .../libusb/libusb/os/linux_udev.c | 321 + .../libusb/libusb/os/linux_usbfs.c | 2877 +++++++++ .../libusb/libusb/os/linux_usbfs.h | 221 + .../libusb/libusb/os/netbsd_usb.c | 619 ++ .../libusb/libusb/os/null_usb.c | 111 + .../libusb/libusb/os/openbsd_usb.c | 702 +++ .../libusb/libusb/os/sunos_usb.c | 1621 +++++ .../libusb/libusb/os/sunos_usb.h | 79 + .../libusb/libusb/os/threads_posix.c | 126 + .../libusb/libusb/os/threads_posix.h | 104 + .../libusb/libusb/os/threads_windows.c | 40 + .../libusb/libusb/os/threads_windows.h | 114 + .../libusb/libusb/os/windows_common.c | 1008 +++ .../libusb/libusb/os/windows_common.h | 443 ++ .../libusb/libusb/os/windows_hotplug.c | 321 + .../libusb/libusb/os/windows_hotplug.h | 28 + .../libusb/libusb/os/windows_usbdk.c | 732 +++ .../libusb/libusb/os/windows_usbdk.h | 106 + .../libusb/libusb/os/windows_winusb.c | 5478 +++++++++++++++++ .../libusb/libusb/os/windows_winusb.h | 792 +++ .../libusb/libusb/strerror.c | 223 + .../libusb1-sys-0.7.0/libusb/libusb/sync.c | 342 + .../libusb1-sys-0.7.0/libusb/libusb/version.h | 18 + .../libusb/libusb/version_nano.h | 1 + .../libusb1-sys-0.7.0/libusb/msvc/Base.props | 60 + .../msvc/Configuration.Application.props | 7 + .../libusb/msvc/Configuration.Base.props | 48 + .../msvc/Configuration.DynamicLibrary.props | 33 + .../msvc/Configuration.StaticLibrary.props | 7 + .../msvc/ProjectConfigurations.Base.props | 122 + .../libusb/msvc/build_all.ps1 | 17 + .../libusb1-sys-0.7.0/libusb/msvc/config.h | 62 + .../libusb/msvc/dpfp.vcxproj | 33 + .../libusb/msvc/dpfp_threaded.vcxproj | 38 + .../libusb/msvc/fxload.vcxproj | 46 + .../libusb/msvc/getopt.vcxproj | 40 + .../libusb/msvc/getopt/bits/getopt_core.h | 96 + .../libusb/msvc/getopt/bits/getopt_ext.h | 77 + .../libusb/msvc/getopt/features.h | 21 + .../libusb/msvc/getopt/getopt.c | 811 +++ .../libusb/msvc/getopt/getopt.h | 38 + .../libusb/msvc/getopt/getopt1.c | 159 + .../libusb/msvc/getopt/getopt_int.h | 118 + .../libusb/msvc/getopt/gettext.h | 7 + .../libusb/msvc/getopt/unistd.h | 5 + .../libusb/msvc/hotplugtest.vcxproj | 32 + .../libusb/msvc/init_context.vcxproj | 35 + .../libusb1-sys-0.7.0/libusb/msvc/libusb.sln | 789 +++ .../libusb/msvc/libusb_dll.vcxproj | 63 + .../libusb/msvc/libusb_static.vcxproj | 51 + .../libusb/msvc/listdevs.vcxproj | 32 + .../libusb/msvc/sam3u_benchmark.vcxproj | 33 + .../libusb/msvc/set_option.vcxproj | 35 + .../libusb/msvc/stress.vcxproj | 35 + .../libusb/msvc/stress_mt.vcxproj | 33 + .../libusb/msvc/testlibusb.vcxproj | 32 + .../libusb/msvc/xusb.vcxproj | 38 + .../vendor/libusb1-sys-0.7.0/src/constants.rs | 160 + .../vendor/libusb1-sys-0.7.0/src/lib.rs | 696 +++ .../vendor/libusb1-sys-0.7.0/tests/test.rs | 160 + .../vendor/rustix-1.1.4/.cargo_vcs_info.json | 6 - rust_backend/vendor/rustix-1.1.4/src/ffi.rs | 23 - .../rustix-1.1.4/src/static_assertions.rs | 36 - .../.cargo-checksum.json | 2 +- .../vendor/rustix-1.1.5/.cargo_vcs_info.json | 6 + .../{rustix-1.1.4 => rustix-1.1.5}/CHANGES.md | 0 .../CODE_OF_CONDUCT.md | 0 .../CONTRIBUTING.md | 0 .../{rustix-1.1.4 => rustix-1.1.5}/COPYRIGHT | 0 .../{rustix-1.1.4 => rustix-1.1.5}/Cargo.lock | 171 +- .../{rustix-1.1.4 => rustix-1.1.5}/Cargo.toml | 6 +- .../vendor/rustix-1.1.5/Cargo.toml.orig | 276 + .../LICENSE-APACHE | 0 .../LICENSE-Apache-2.0_WITH_LLVM-exception | 0 .../LICENSE-MIT | 0 .../ORG_CODE_OF_CONDUCT.md | 0 .../{rustix-1.1.4 => rustix-1.1.5}/README.md | 17 +- .../SECURITY.md | 0 .../benches/mod.rs | 0 .../{rustix-1.1.4 => rustix-1.1.5}/build.rs | 23 +- .../src/backend/libc/c.rs | 51 +- .../src/backend/libc/conv.rs | 12 +- .../src/backend/libc/event/epoll.rs | 0 .../src/backend/libc/event/mod.rs | 0 .../src/backend/libc/event/poll_fd.rs | 7 +- .../src/backend/libc/event/syscalls.rs | 12 +- .../src/backend/libc/event/types.rs | 0 .../backend/libc/event/windows_syscalls.rs | 9 +- .../src/backend/libc/fs/dir.rs | 4 +- .../src/backend/libc/fs/inotify.rs | 0 .../src/backend/libc/fs/makedev.rs | 0 .../src/backend/libc/fs/mod.rs | 5 +- .../src/backend/libc/fs/syscalls.rs | 24 +- .../src/backend/libc/fs/types.rs | 10 + .../src/backend/libc/io/errno.rs | 0 .../src/backend/libc/io/mod.rs | 0 .../src/backend/libc/io/syscalls.rs | 1 + .../src/backend/libc/io/types.rs | 0 .../src/backend/libc/io/windows_syscalls.rs | 0 .../src/backend/libc/io_uring/mod.rs | 0 .../src/backend/libc/io_uring/syscalls.rs | 0 .../src/backend/libc/mm/mod.rs | 0 .../src/backend/libc/mm/syscalls.rs | 0 .../src/backend/libc/mm/types.rs | 0 .../src/backend/libc/mod.rs | 0 .../src/backend/libc/mount/mod.rs | 0 .../src/backend/libc/mount/syscalls.rs | 0 .../src/backend/libc/mount/types.rs | 0 .../src/backend/libc/net/addr.rs | 3 + .../src/backend/libc/net/ext.rs | 0 .../src/backend/libc/net/mod.rs | 0 .../src/backend/libc/net/msghdr.rs | 0 .../src/backend/libc/net/netdevice.rs | 0 .../src/backend/libc/net/read_sockaddr.rs | 0 .../src/backend/libc/net/send_recv.rs | 0 .../src/backend/libc/net/sockopt.rs | 26 +- .../src/backend/libc/net/syscalls.rs | 0 .../src/backend/libc/net/write_sockaddr.rs | 0 .../src/backend/libc/param/auxv.rs | 0 .../src/backend/libc/param/mod.rs | 0 .../src/backend/libc/pid/mod.rs | 0 .../src/backend/libc/pid/syscalls.rs | 0 .../src/backend/libc/pipe/mod.rs | 0 .../src/backend/libc/pipe/syscalls.rs | 1 + .../src/backend/libc/pipe/types.rs | 4 +- .../src/backend/libc/prctl/mod.rs | 0 .../src/backend/libc/prctl/syscalls.rs | 0 .../src/backend/libc/process/mod.rs | 0 .../src/backend/libc/process/syscalls.rs | 2 + .../src/backend/libc/process/types.rs | 1 + .../src/backend/libc/process/wait.rs | 0 .../src/backend/libc/pty/mod.rs | 0 .../src/backend/libc/pty/syscalls.rs | 0 .../src/backend/libc/rand/mod.rs | 0 .../src/backend/libc/rand/syscalls.rs | 0 .../src/backend/libc/rand/types.rs | 0 .../src/backend/libc/shm/mod.rs | 0 .../src/backend/libc/shm/syscalls.rs | 0 .../src/backend/libc/shm/types.rs | 0 .../src/backend/libc/system/mod.rs | 0 .../src/backend/libc/system/syscalls.rs | 1 + .../src/backend/libc/system/types.rs | 0 .../src/backend/libc/termios/mod.rs | 0 .../src/backend/libc/termios/syscalls.rs | 0 .../src/backend/libc/termios/types.rs | 0 .../src/backend/libc/thread/cpu_set.rs | 0 .../src/backend/libc/thread/futex.rs | 0 .../src/backend/libc/thread/mod.rs | 0 .../src/backend/libc/thread/syscalls.rs | 1 + .../src/backend/libc/thread/types.rs | 0 .../src/backend/libc/time/mod.rs | 0 .../src/backend/libc/time/syscalls.rs | 0 .../src/backend/libc/time/types.rs | 8 +- .../src/backend/libc/ugid/mod.rs | 0 .../src/backend/libc/ugid/syscalls.rs | 0 .../src/backend/libc/winsock_c.rs | 0 .../src/backend/linux_raw/arch/aarch64.rs | 0 .../src/backend/linux_raw/arch/arm.rs | 0 .../src/backend/linux_raw/arch/mips.rs | 0 .../src/backend/linux_raw/arch/mips32r6.rs | 0 .../src/backend/linux_raw/arch/mips64.rs | 0 .../src/backend/linux_raw/arch/mips64r6.rs | 0 .../src/backend/linux_raw/arch/mod.rs | 0 .../src/backend/linux_raw/arch/powerpc.rs | 0 .../src/backend/linux_raw/arch/powerpc64.rs | 0 .../src/backend/linux_raw/arch/riscv64.rs | 0 .../src/backend/linux_raw/arch/s390x.rs | 0 .../src/backend/linux_raw/arch/thumb.rs | 0 .../src/backend/linux_raw/arch/x86.rs | 0 .../src/backend/linux_raw/arch/x86_64.rs | 0 .../src/backend/linux_raw/c.rs | 0 .../src/backend/linux_raw/conv.rs | 0 .../src/backend/linux_raw/event/epoll.rs | 0 .../src/backend/linux_raw/event/mod.rs | 0 .../src/backend/linux_raw/event/poll_fd.rs | 0 .../src/backend/linux_raw/event/syscalls.rs | 0 .../src/backend/linux_raw/event/types.rs | 0 .../src/backend/linux_raw/fs/dir.rs | 0 .../src/backend/linux_raw/fs/inotify.rs | 0 .../src/backend/linux_raw/fs/makedev.rs | 0 .../src/backend/linux_raw/fs/mod.rs | 0 .../src/backend/linux_raw/fs/syscalls.rs | 14 +- .../src/backend/linux_raw/fs/types.rs | 3 +- .../src/backend/linux_raw/io/errno.rs | 6 +- .../src/backend/linux_raw/io/mod.rs | 0 .../src/backend/linux_raw/io/syscalls.rs | 0 .../src/backend/linux_raw/io/types.rs | 0 .../src/backend/linux_raw/io_uring/mod.rs | 0 .../backend/linux_raw/io_uring/syscalls.rs | 0 .../src/backend/linux_raw/mm/mod.rs | 0 .../src/backend/linux_raw/mm/syscalls.rs | 0 .../src/backend/linux_raw/mm/types.rs | 0 .../src/backend/linux_raw/mod.rs | 0 .../src/backend/linux_raw/mount/mod.rs | 0 .../src/backend/linux_raw/mount/syscalls.rs | 0 .../src/backend/linux_raw/mount/types.rs | 0 .../src/backend/linux_raw/net/addr.rs | 3 + .../src/backend/linux_raw/net/mod.rs | 0 .../src/backend/linux_raw/net/msghdr.rs | 0 .../src/backend/linux_raw/net/netdevice.rs | 0 .../backend/linux_raw/net/read_sockaddr.rs | 0 .../src/backend/linux_raw/net/send_recv.rs | 0 .../src/backend/linux_raw/net/sockopt.rs | 29 +- .../src/backend/linux_raw/net/syscalls.rs | 0 .../backend/linux_raw/net/write_sockaddr.rs | 0 .../src/backend/linux_raw/param/auxv.rs | 0 .../src/backend/linux_raw/param/init.rs | 0 .../src/backend/linux_raw/param/libc_auxv.rs | 24 +- .../src/backend/linux_raw/param/mod.rs | 0 .../src/backend/linux_raw/pid/mod.rs | 0 .../src/backend/linux_raw/pid/syscalls.rs | 0 .../src/backend/linux_raw/pipe/mod.rs | 0 .../src/backend/linux_raw/pipe/syscalls.rs | 0 .../src/backend/linux_raw/pipe/types.rs | 0 .../src/backend/linux_raw/prctl/mod.rs | 0 .../src/backend/linux_raw/prctl/syscalls.rs | 0 .../src/backend/linux_raw/process/mod.rs | 0 .../src/backend/linux_raw/process/syscalls.rs | 0 .../src/backend/linux_raw/process/types.rs | 0 .../src/backend/linux_raw/process/wait.rs | 0 .../src/backend/linux_raw/pty/mod.rs | 0 .../src/backend/linux_raw/pty/syscalls.rs | 0 .../src/backend/linux_raw/rand/mod.rs | 0 .../src/backend/linux_raw/rand/syscalls.rs | 0 .../src/backend/linux_raw/rand/types.rs | 0 .../src/backend/linux_raw/reg.rs | 0 .../src/backend/linux_raw/runtime/mod.rs | 0 .../src/backend/linux_raw/runtime/syscalls.rs | 0 .../src/backend/linux_raw/runtime/tls.rs | 0 .../src/backend/linux_raw/shm/mod.rs | 0 .../src/backend/linux_raw/shm/syscalls.rs | 0 .../src/backend/linux_raw/shm/types.rs | 0 .../src/backend/linux_raw/system/mod.rs | 0 .../src/backend/linux_raw/system/syscalls.rs | 0 .../src/backend/linux_raw/system/types.rs | 0 .../src/backend/linux_raw/termios/mod.rs | 0 .../src/backend/linux_raw/termios/syscalls.rs | 0 .../src/backend/linux_raw/termios/types.rs | 0 .../src/backend/linux_raw/thread/cpu_set.rs | 0 .../src/backend/linux_raw/thread/futex.rs | 0 .../src/backend/linux_raw/thread/mod.rs | 0 .../src/backend/linux_raw/thread/syscalls.rs | 0 .../src/backend/linux_raw/thread/types.rs | 0 .../src/backend/linux_raw/time/mod.rs | 0 .../src/backend/linux_raw/time/syscalls.rs | 0 .../src/backend/linux_raw/time/types.rs | 0 .../src/backend/linux_raw/ugid/mod.rs | 0 .../src/backend/linux_raw/ugid/syscalls.rs | 0 .../src/backend/linux_raw/vdso.rs | 0 .../src/backend/linux_raw/vdso_wrappers.rs | 0 .../src/bitcast.rs | 0 .../src/buffer.rs | 0 .../src/check_types.rs | 16 +- .../src/clockid.rs | 0 .../src/cstr.rs | 0 .../src/event/epoll.rs | 0 .../src/event/eventfd.rs | 0 .../src/event/kqueue.rs | 0 .../src/event/mod.rs | 0 .../src/event/pause.rs | 0 .../src/event/poll.rs | 0 .../src/event/port.rs | 0 .../src/event/select.rs | 0 rust_backend/vendor/rustix-1.1.5/src/ffi.rs | 16 + .../src/fs/abs.rs | 0 .../src/fs/at.rs | 10 +- .../src/fs/constants.rs | 6 +- .../src/fs/copy_file_range.rs | 0 .../src/fs/dir.rs | 0 .../src/fs/fadvise.rs | 0 .../src/fs/fcntl.rs | 0 .../src/fs/fcntl_apple.rs | 0 .../src/fs/fcopyfile.rs | 0 .../src/fs/fd.rs | 0 .../src/fs/getpath.rs | 0 .../src/fs/id.rs | 0 .../src/fs/inotify.rs | 0 .../src/fs/ioctl.rs | 0 .../src/fs/makedev.rs | 0 .../src/fs/memfd_create.rs | 0 .../src/fs/mod.rs | 10 +- .../src/fs/openat2.rs | 0 .../src/fs/raw_dir.rs | 0 .../src/fs/seek_from.rs | 0 .../src/fs/sendfile.rs | 0 .../src/fs/special.rs | 0 .../src/fs/statx.rs | 0 .../src/fs/sync.rs | 0 .../src/fs/xattr.rs | 0 .../src/io/close.rs | 0 .../src/io/dup.rs | 0 .../src/io/errno.rs | 0 .../src/io/fcntl.rs | 0 .../src/io/ioctl.rs | 0 .../src/io/mod.rs | 0 .../src/io/read_write.rs | 0 .../src/io_uring/bindgen_types.rs | 0 .../src/io_uring/mod.rs | 8 +- .../src/ioctl/bsd.rs | 0 .../src/ioctl/linux.rs | 0 .../src/ioctl/mod.rs | 0 .../src/ioctl/patterns.rs | 0 .../src/kernel_sigset.rs | 0 .../{rustix-1.1.4 => rustix-1.1.5}/src/lib.rs | 32 +- .../src/maybe_polyfill/no_std/io/mod.rs | 0 .../src/maybe_polyfill/no_std/mod.rs | 0 .../src/maybe_polyfill/no_std/net/mod.rs | 0 .../src/maybe_polyfill/no_std/os/fd/mod.rs | 0 .../src/maybe_polyfill/no_std/os/fd/owned.rs | 12 +- .../src/maybe_polyfill/no_std/os/fd/raw.rs | 0 .../src/maybe_polyfill/no_std/os/mod.rs | 0 .../no_std/os/windows/io/mod.rs | 0 .../no_std/os/windows/io/raw.rs | 0 .../no_std/os/windows/io/socket.rs | 40 +- .../maybe_polyfill/no_std/os/windows/mod.rs | 0 .../src/maybe_polyfill/std/mod.rs | 0 .../src/mm/madvise.rs | 0 .../src/mm/mmap.rs | 0 .../src/mm/mod.rs | 0 .../src/mm/msync.rs | 0 .../src/mm/userfaultfd.rs | 0 .../src/mount/fsopen.rs | 0 .../src/mount/mod.rs | 0 .../src/mount/mount_unmount.rs | 0 .../src/mount/types.rs | 0 .../src/msan.rs | 0 .../src/net/addr.rs | 2 +- .../src/net/mod.rs | 0 .../src/net/netdevice.rs | 0 .../src/net/send_recv/mod.rs | 0 .../src/net/send_recv/msg.rs | 0 .../src/net/socket.rs | 0 .../src/net/socket_addr_any.rs | 0 .../src/net/socketpair.rs | 0 .../src/net/sockopt.rs | 7 +- .../src/net/types.rs | 29 +- .../src/net/wsa.rs | 0 .../src/not_implemented.rs | 0 .../src/param/auxv.rs | 0 .../src/param/init.rs | 0 .../src/param/mod.rs | 0 .../src/path/arg.rs | 0 .../src/path/dec_int.rs | 0 .../src/path/mod.rs | 0 .../{rustix-1.1.4 => rustix-1.1.5}/src/pid.rs | 10 +- .../src/pipe.rs | 1 + .../src/prctl.rs | 0 .../src/process/chdir.rs | 0 .../src/process/chroot.rs | 0 .../src/process/exit.rs | 0 .../src/process/fcntl_getlk.rs | 0 .../src/process/id.rs | 0 .../src/process/ioctl.rs | 24 +- .../src/process/kill.rs | 0 .../src/process/mod.rs | 15 +- .../src/process/pidfd.rs | 0 .../src/process/pidfd_getfd.rs | 0 .../src/process/pivot_root.rs | 0 .../src/process/prctl.rs | 0 .../src/process/priority.rs | 0 .../src/process/procctl.rs | 0 .../src/process/rlimit.rs | 0 .../src/process/types.rs | 3 + .../src/process/umask.rs | 0 .../src/process/wait.rs | 0 .../{rustix-1.1.4 => rustix-1.1.5}/src/pty.rs | 0 .../src/rand/getrandom.rs | 0 .../src/rand/mod.rs | 0 .../src/runtime_448b8ad740e2a26f.rs} | 12 +- .../{rustix-1.1.4 => rustix-1.1.5}/src/shm.rs | 0 .../src/signal.rs | 0 .../src/stdio.rs | 0 .../src/system.rs | 7 +- .../src/termios/ioctl.rs | 0 .../src/termios/mod.rs | 0 .../src/termios/tc.rs | 0 .../src/termios/tty.rs | 0 .../src/termios/types.rs | 44 +- .../src/thread/clock.rs | 0 .../src/thread/futex.rs | 0 .../src/thread/id.rs | 0 .../src/thread/libcap.rs | 0 .../src/thread/membarrier.rs | 0 .../src/thread/mod.rs | 0 .../src/thread/prctl.rs | 0 .../src/thread/sched.rs | 0 .../src/thread/sched_yield.rs | 0 .../src/thread/setns.rs | 0 .../src/time/clock.rs | 0 .../src/time/mod.rs | 0 .../src/time/timerfd.rs | 0 .../src/timespec.rs | 73 +- .../src/ugid.rs | 8 +- .../src/utils.rs | 2 +- .../src/weak.rs | 0 441 files changed, 43030 insertions(+), 520 deletions(-) create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/.cargo-checksum.json create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/.cargo_vcs_info.json create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/CHANGELOG.md create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/Cargo.lock create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml.orig create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/LICENSE create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/README.md create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/build.rs create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/AUTHORS create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/COPYING create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/README create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/README.md create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am.extra create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.in create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/core.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/descriptor.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/hotplug.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/io.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.def create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.rc create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusbi.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/emscripten_webusb.cpp create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_pollfs.cpp create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_backend.cpp create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.cpp create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_netlink.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_udev.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/netbsd_usb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/null_usb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/openbsd_usb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/strerror.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/sync.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version_nano.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Base.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Application.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Base.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.DynamicLibrary.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.StaticLibrary.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/ProjectConfigurations.Base.props create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/build_all.ps1 create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/config.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp_threaded.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/fxload.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_core.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_ext.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/features.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt1.c create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt_int.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/gettext.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/unistd.h create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/hotplugtest.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/init_context.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb.sln create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_dll.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_static.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/listdevs.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/sam3u_benchmark.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/set_option.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress_mt.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/testlibusb.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/xusb.vcxproj create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/src/constants.rs create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/src/lib.rs create mode 100644 rust_backend/vendor/libusb1-sys-0.7.0/tests/test.rs delete mode 100644 rust_backend/vendor/rustix-1.1.4/.cargo_vcs_info.json delete mode 100644 rust_backend/vendor/rustix-1.1.4/src/ffi.rs delete mode 100644 rust_backend/vendor/rustix-1.1.4/src/static_assertions.rs rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/.cargo-checksum.json (74%) create mode 100644 rust_backend/vendor/rustix-1.1.5/.cargo_vcs_info.json rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/CHANGES.md (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/CODE_OF_CONDUCT.md (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/CONTRIBUTING.md (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/COPYRIGHT (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/Cargo.lock (88%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/Cargo.toml (99%) create mode 100644 rust_backend/vendor/rustix-1.1.5/Cargo.toml.orig rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/LICENSE-APACHE (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/LICENSE-Apache-2.0_WITH_LLVM-exception (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/LICENSE-MIT (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/ORG_CODE_OF_CONDUCT.md (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/README.md (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/SECURITY.md (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/benches/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/build.rs (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/c.rs (96%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/conv.rs (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/epoll.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/poll_fd.rs (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/syscalls.rs (97%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/event/windows_syscalls.rs (85%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/dir.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/inotify.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/makedev.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/mod.rs (86%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/syscalls.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/fs/types.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io/errno.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io/windows_syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io_uring/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/io_uring/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mm/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mm/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mm/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mount/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mount/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/mount/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/addr.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/ext.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/msghdr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/netdevice.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/read_sockaddr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/send_recv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/sockopt.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/net/write_sockaddr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/param/auxv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/param/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pid/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pid/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pipe/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pipe/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pipe/types.rs (96%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/prctl/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/prctl/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/process/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/process/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/process/types.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/process/wait.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pty/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/pty/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/rand/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/rand/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/rand/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/shm/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/shm/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/shm/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/system/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/system/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/system/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/termios/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/termios/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/termios/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/thread/cpu_set.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/thread/futex.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/thread/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/thread/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/thread/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/time/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/time/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/time/types.rs (96%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/ugid/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/ugid/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/libc/winsock_c.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/aarch64.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/arm.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/mips.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/mips32r6.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/mips64.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/mips64r6.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/powerpc.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/powerpc64.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/riscv64.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/s390x.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/thumb.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/x86.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/arch/x86_64.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/c.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/conv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/event/epoll.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/event/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/event/poll_fd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/event/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/event/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/dir.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/inotify.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/makedev.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/syscalls.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/fs/types.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io/errno.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io_uring/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/io_uring/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mm/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mm/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mm/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mount/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mount/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/mount/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/addr.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/msghdr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/netdevice.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/read_sockaddr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/send_recv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/sockopt.rs (97%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/net/write_sockaddr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/param/auxv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/param/init.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/param/libc_auxv.rs (82%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/param/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pid/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pid/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pipe/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pipe/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pipe/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/prctl/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/prctl/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/process/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/process/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/process/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/process/wait.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pty/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/pty/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/rand/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/rand/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/rand/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/reg.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/runtime/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/runtime/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/runtime/tls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/shm/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/shm/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/shm/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/system/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/system/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/system/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/termios/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/termios/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/termios/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/thread/cpu_set.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/thread/futex.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/thread/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/thread/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/thread/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/time/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/time/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/time/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/ugid/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/ugid/syscalls.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/vdso.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/backend/linux_raw/vdso_wrappers.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/bitcast.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/buffer.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/check_types.rs (90%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/clockid.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/cstr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/epoll.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/eventfd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/kqueue.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/pause.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/poll.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/port.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/event/select.rs (100%) create mode 100644 rust_backend/vendor/rustix-1.1.5/src/ffi.rs rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/abs.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/at.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/constants.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/copy_file_range.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/dir.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/fadvise.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/fcntl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/fcntl_apple.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/fcopyfile.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/fd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/getpath.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/id.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/inotify.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/ioctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/makedev.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/memfd_create.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/mod.rs (92%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/openat2.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/raw_dir.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/seek_from.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/sendfile.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/special.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/statx.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/sync.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/fs/xattr.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/close.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/dup.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/errno.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/fcntl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/ioctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io/read_write.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io_uring/bindgen_types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/io_uring/mod.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/ioctl/bsd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/ioctl/linux.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/ioctl/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/ioctl/patterns.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/kernel_sigset.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/lib.rs (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/io/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/net/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/fd/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/fd/owned.rs (96%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/fd/raw.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/windows/io/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/windows/io/raw.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/windows/io/socket.rs (87%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/no_std/os/windows/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/maybe_polyfill/std/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mm/madvise.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mm/mmap.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mm/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mm/msync.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mm/userfaultfd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mount/fsopen.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mount/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mount/mount_unmount.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/mount/types.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/msan.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/addr.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/netdevice.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/send_recv/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/send_recv/msg.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/socket.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/socket_addr_any.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/socketpair.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/sockopt.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/types.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/net/wsa.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/not_implemented.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/param/auxv.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/param/init.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/param/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/path/arg.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/path/dec_int.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/path/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/pid.rs (94%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/pipe.rs (99%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/prctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/chdir.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/chroot.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/exit.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/fcntl_getlk.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/id.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/ioctl.rs (75%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/kill.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/mod.rs (91%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/pidfd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/pidfd_getfd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/pivot_root.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/prctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/priority.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/procctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/rlimit.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/types.rs (96%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/umask.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/process/wait.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/pty.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/rand/getrandom.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/rand/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4/src/runtime.rs => rustix-1.1.5/src/runtime_448b8ad740e2a26f.rs} (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/shm.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/signal.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/stdio.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/system.rs (98%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/termios/ioctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/termios/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/termios/tc.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/termios/tty.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/termios/types.rs (97%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/clock.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/futex.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/id.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/libcap.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/membarrier.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/prctl.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/sched.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/sched_yield.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/thread/setns.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/time/clock.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/time/mod.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/time/timerfd.rs (100%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/timespec.rs (80%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/ugid.rs (94%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/utils.rs (95%) rename rust_backend/vendor/{rustix-1.1.4 => rustix-1.1.5}/src/weak.rs (100%) diff --git a/.gitattributes b/.gitattributes index 3247dbf0..03098508 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,7 @@ gradlew text eol=lf android/app/src/test/resources/lyrics_usability_cases.tsv whitespace=-blank-at-eol # Retain upstream dependency sources for the Android compatibility patch. -rust_backend/vendor/** linguist-vendored +rust_backend/vendor/** linguist-vendored -text third_party/audio_service/** linguist-vendored # Keep the pinned package byte-for-byte outside the documented native patch. third_party/audio_service/CHANGELOG.md whitespace=-blank-at-eol @@ -22,8 +22,10 @@ third_party/audio_service/android/src/main/java/com/ryanheise/audioservice/Audio third_party/audio_service/darwin/audio_service/Sources/audio_service/AudioServicePlugin.m whitespace=-blank-at-eol third_party/audio_service/darwin/audio_service.podspec whitespace=-blank-at-eof # Preserve the original whitespace in upstream license and policy documents. -rust_backend/vendor/rustix-1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception whitespace=-blank-at-eof -rust_backend/vendor/rustix-1.1.4/ORG_CODE_OF_CONDUCT.md whitespace=-blank-at-eol +rust_backend/vendor/rustix-1.1.5/LICENSE-Apache-2.0_WITH_LLVM-exception whitespace=-blank-at-eof +rust_backend/vendor/rustix-1.1.5/ORG_CODE_OF_CONDUCT.md whitespace=-blank-at-eol +# Preserve libusb's upstream sources, license whitespace, and source hashes. +rust_backend/vendor/libusb1-sys-0.7.0/** -whitespace # Binary files *.png binary diff --git a/assets/licenses/usb.txt b/assets/licenses/usb.txt index 5ea53e9f..33a73409 100644 --- a/assets/licenses/usb.txt +++ b/assets/licenses/usb.txt @@ -1,8 +1,9 @@ USB direct audio dependencies -libusb1-sys 0.7.0 (MIT) and its bundled libusb (LGPL-2.1-or-later). +libusb1-sys 0.7.0 (MIT), bundled with libusb 1.0.30 (LGPL-2.1-or-later). Source: https://crates.io/crates/libusb1-sys/0.7.0 -The unmodified dependency is pinned in rust_backend/Cargo.lock. Source for +libusb source: https://github.com/libusb/libusb/releases/tag/v1.0.30 +The dependency and updated C sources are pinned in rust_backend/vendor. Source for SpotiFLAC and scripts/build_rust_backend.sh is available at https://github.com/spotiflacapp/SpotiFLAC-Mobile for rebuilding/relinking the Android native library with a modified libusb. This component's LGPL rights, diff --git a/rust_backend/Cargo.lock b/rust_backend/Cargo.lock index ed46e9ce..ee15fa5c 100644 --- a/rust_backend/Cargo.lock +++ b/rust_backend/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.48" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb61aea1a7def73ee7c350a184f0e70b32c182344e2e75bf70c9b621b83417fd" +checksum = "ee19bd99b43e3691acbad4e840420a4881cea6c0b66a208125a824f8fd53f5a1" dependencies = [ "compression-codecs", "compression-core", @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.5.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" +checksum = "50a649af8a827553c29fb0cb4bd4a6f1a0dd695bd3232b9bc98bd9c8a3ffbb8b" dependencies = [ "find-msvc-tools", "shlex 2.0.1", @@ -390,6 +390,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "chacha20" version = "0.10.2" @@ -465,7 +471,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -504,9 +510,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9" +checksum = "98fc98460ba0ad5317075d3632b8dfc45d0be8c4a49347c2a38272019717614a" dependencies = [ "compression-core", "flate2", @@ -621,7 +627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a" dependencies = [ "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -635,9 +641,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" dependencies = [ "cmov", ] @@ -709,7 +715,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -735,9 +741,9 @@ checksum = "b04dc5a38e4f151a79d9f2451ae6037fb6eaf5cba34771f44781f80e508498e3" [[package]] name = "either" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" [[package]] name = "equivalent" @@ -822,9 +828,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a" +checksum = "b5c95b673b8f6f7235229ae11c5642d81b04c2e64c1e2fb417bc0cf73ca45f29" dependencies = [ "autocfg", ] @@ -863,7 +869,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -952,9 +958,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" dependencies = [ "atomic-waker", "bytes", @@ -1099,9 +1105,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" dependencies = [ "atomic-waker", "bytes", @@ -1436,15 +1442,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" [[package]] name = "libc" -version = "0.2.189" +version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" [[package]] name = "libloading" @@ -1470,8 +1476,6 @@ dependencies = [ [[package]] name = "libusb1-sys" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" dependencies = [ "cc", "libc", @@ -1567,9 +1571,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" dependencies = [ "libc", "wasi", @@ -1592,6 +1596,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1812,9 +1828,9 @@ dependencies = [ [[package]] name = "powerfmt" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" [[package]] name = "precomputed-hash" @@ -1948,9 +1964,9 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "relative-path" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0" +checksum = "f2a073568cd8b9c4825429d3e9fc5f52851b4f97cc1348800689408921e6b8f8" dependencies = [ "serde", ] @@ -2011,7 +2027,7 @@ dependencies = [ "proc-macro2", "quote", "rquickjs-core", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -2075,7 +2091,7 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" dependencies = [ "bitflags", "errno", @@ -2319,14 +2335,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "1741ab7a6cc54a03a89b5d563ed60075c277d9e3cfa73ad0c1f23f23974703c6" dependencies = [ "itoa", "memchr", @@ -2337,9 +2353,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" dependencies = [ "serde_core", ] @@ -2423,9 +2439,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" [[package]] name = "smawk" @@ -2648,9 +2664,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +checksum = "d62a2e0561533f2ca2561d0cf27fd9fedb640a1bf2616ff5d5c80d99017faadc" dependencies = [ "proc-macro2", "quote", @@ -2676,7 +2692,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -2728,7 +2744,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] @@ -2779,9 +2795,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.53.1" +version = "1.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" dependencies = [ "bytes", "libc", @@ -2800,14 +2816,14 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] name = "tokio-rustls" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" dependencies = [ "rustls", "tokio", @@ -2815,9 +2831,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" dependencies = [ "bytes", "futures-core", @@ -2829,9 +2845,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.6+spec-1.1.0" +version = "1.1.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" +checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d" dependencies = [ "indexmap", "serde_core", @@ -2844,18 +2860,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.15+spec-1.1.0" +version = "0.25.17+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" dependencies = [ "indexmap", "toml_datetime", @@ -2865,18 +2881,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.3+spec-1.1.0" +version = "1.1.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" +checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770" [[package]] name = "tower-service" @@ -3139,9 +3155,9 @@ dependencies = [ [[package]] name = "want" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" dependencies = [ "try-lock", ] @@ -3184,7 +3200,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", "wasm-bindgen-shared", ] @@ -3532,13 +3548,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", "synstructure 0.14.0", ] @@ -3559,15 +3575,15 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", "synstructure 0.14.0", ] [[package]] name = "zeroize" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" [[package]] name = "zerotrie" @@ -3599,19 +3615,20 @@ checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.7", ] [[package]] name = "zip" -version = "8.6.0" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +checksum = "e49ade69c4434c55dd651d36fe05f38e0578db948b773cbc01c48c4b5e560692" dependencies = [ "crc32fast", "flate2", "indexmap", "memchr", + "nix", "typed-path", ] diff --git a/rust_backend/Cargo.toml b/rust_backend/Cargo.toml index 8143cb18..58f88186 100644 --- a/rust_backend/Cargo.toml +++ b/rust_backend/Cargo.toml @@ -1,17 +1,18 @@ [workspace] members = ["crates/core", "crates/network", "crates/providers", "crates/extensions", "crates/mobile", "crates/bindgen"] -exclude = ["vendor/rustix-1.1.4"] +exclude = ["vendor/rustix-1.1.5", "vendor/libusb1-sys-0.7.0"] resolver = "3" [patch.crates-io] # Android < 30 may kill processes that probe statx directly (SIGSYS). # Keep all direct and transitive users on the same compatibility fix. -rustix = { path = "vendor/rustix-1.1.4" } +rustix = { path = "vendor/rustix-1.1.5" } +libusb1-sys = { path = "vendor/libusb1-sys-0.7.0" } [workspace.package] version = "0.1.0" edition = "2024" -rust-version = "1.98.1" +rust-version = "1.99.0" license = "MIT" publish = false @@ -33,13 +34,13 @@ blowfish = { version = "=0.10.0", features = ["zeroize"] } zeroize = "1.8" getrandom = "0.4" tempfile = "3.27" -zip = { version = "=8.6.0", default-features = false, features = ["deflate-flate2-zlib-rs"] } +zip = { version = "=9.0.1", default-features = false, features = ["deflate-flate2-zlib-rs"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["float_roundtrip", "raw_value"] } thiserror = "2.0" uniffi = { version = "=0.32.2", default-features = false } -tokio = { version = "=1.53.1", features = ["rt-multi-thread", "net", "time", "sync", "io-util", "macros"] } -hyper = { version = "=1.11.1", features = ["client", "http1", "http2"] } +tokio = { version = "=1.53.2", features = ["rt-multi-thread", "net", "time", "sync", "io-util", "macros"] } +hyper = { version = "=1.12.0", features = ["client", "http1", "http2"] } hyper-util = { version = "=0.1.21", features = ["client-legacy", "http1", "http2", "tokio"] } hyper-rustls = { version = "=0.27.10", default-features = false, features = ["http1", "http2", "ring", "tls12"] } rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } @@ -70,7 +71,7 @@ chrono = { version = "=0.4.45", default-features = false, features = ["clock"] } tz-rs = "=0.7.3" httpdate = "=1.0.3" cap-std = "=4.0.3" -rustix = { version = "=1.1.4", features = ["fs"] } +rustix = { version = "=1.1.5", features = ["fs"] } [workspace.lints.rust] unsafe_code = "deny" diff --git a/rust_backend/crates/extensions/Cargo.toml b/rust_backend/crates/extensions/Cargo.toml index 69c7f225..37df3602 100644 --- a/rust_backend/crates/extensions/Cargo.toml +++ b/rust_backend/crates/extensions/Cargo.toml @@ -35,7 +35,7 @@ cap-std.workspace = true rustix.workspace = true [dev-dependencies] -libc = "=0.2.189" +libc = "=0.2.190" rustls.workspace = true rcgen = { version = "=0.14.10", default-features = false, features = ["crypto", "ring", "pem"] } diff --git a/rust_backend/crates/providers/src/musicbrainz.rs b/rust_backend/crates/providers/src/musicbrainz.rs index f19bd523..289b5546 100644 --- a/rust_backend/crates/providers/src/musicbrainz.rs +++ b/rust_backend/crates/providers/src/musicbrainz.rs @@ -198,7 +198,7 @@ impl MusicBrainzClient { .filter(|flight| { flight .waiters - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |count| { + .try_update(Ordering::AcqRel, Ordering::Acquire, |count| { (count > 0).then_some(count + 1) }) .is_ok() diff --git a/rust_backend/crates/providers/src/resolver/http.rs b/rust_backend/crates/providers/src/resolver/http.rs index dd29f7c0..964090ee 100644 --- a/rust_backend/crates/providers/src/resolver/http.rs +++ b/rust_backend/crates/providers/src/resolver/http.rs @@ -132,7 +132,7 @@ impl ResolverHttp { self.timeout, check, ) - .map_err(&failure)?; + .map_err(failure)?; let status = stream.response.status; if language.is_none() && status != 200 && !(post && status == 201) { return Err(ResolverError::Failed(format!("HTTP {status}"))); @@ -172,7 +172,7 @@ impl ResolverHttp { let capacity = buffer.len().min(limit + 1 - body.len()); let read = stream .read(&mut buffer[..capacity], check) - .map_err(&failure)?; + .map_err(failure)?; if read == 0 { break; } diff --git a/rust_backend/rust-toolchain.toml b/rust_backend/rust-toolchain.toml index c3f67b67..b432996f 100644 --- a/rust_backend/rust-toolchain.toml +++ b/rust_backend/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.98.1" +channel = "1.99.0" profile = "minimal" components = ["rustfmt", "clippy"] diff --git a/rust_backend/vendor/README.md b/rust_backend/vendor/README.md index cfe815d6..ddfb1475 100644 --- a/rust_backend/vendor/README.md +++ b/rust_backend/vendor/README.md @@ -1,10 +1,10 @@ # Rustix Android compatibility patch -`rustix-1.1.4` is vendored from the pinned crates.io package using `cargo vendor`. +`rustix-1.1.5` is vendored from the pinned crates.io package using `cargo vendor`. Its upstream license files are retained. Cargo's workspace patch applies to both direct users and transitive dependencies such as `cap-std`. The original package checksum is -`b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190`. +`891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d`. The sole upstream source change is in `src/backend/libc/fs/syscalls.rs`: Android uses `weakcall!` for `statx`, rather than `weak_or_syscall!`. Bionic exports this @@ -28,3 +28,19 @@ symbol and blocks the raw syscall to exercise the legacy fallback. A final probe deliberately exits with `SIGSYS` to verify that the filter is active. Neither the symbol wrapper nor the filter is included in the application. This simulated sandbox check complements testing the APK on an actual older ARM32 device. + +# libusb native source update + +`libusb1-sys-0.7.0` is the latest stable Rust binding. Its upstream bundle is +libusb 1.0.27, so the workspace patch replaces the C source directory and MSVC +configuration with the official libusb 1.0.30 release. The Rust API and bindings +are unchanged; `build.rs` reports 1.0.30 when compiling the vendored C library. +The Android dependency keeps its `vendored` feature, so both APK ABIs use these +sources instead of an arbitrary host installation. Upstream licenses are retained. + +Source: https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2 +SHA-256: `fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf` + +The `.cargo-checksum.json` file lists the updated local source hashes while +retaining the original registry package checksum as provenance. The same applies +to the Rustix compatibility patch above. diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/.cargo-checksum.json b/rust_backend/vendor/libusb1-sys-0.7.0/.cargo-checksum.json new file mode 100644 index 00000000..509cb81f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/.cargo-checksum.json @@ -0,0 +1 @@ +{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"6b8d40d082d2128d22ecdb48aaf2a0df27bc65b2711f45d970da950b245aa8cd","CHANGELOG.md":"214a0fe6a85bc7e3d50c06aa0aa0ded81fbd9135f2b20e36e922725b088603a9","Cargo.lock":"a3be52c6f430b6b3b6b7b98e6d217c9ff811be7bfe73c99acf8d896473cce222","Cargo.toml":"ccd78a0b446d7157c26d42f8c15b750826ce75c2226ccfb8397e672a4cf6fe0e","Cargo.toml.orig":"b65c0545773db1b9f3c2bf9cca86665be12bdb2954cbbdc46ce4f09aa0257731","LICENSE":"d6b63b280f7998e4a5e9e21c7b0d2dbe79426e1b28a75e39e41bb8feae0f8b03","README.md":"d30d9e32743be8ed4a70403f574f12760d361d888e413729cc7c3bd050e04fd1","build.rs":"8bc9e8254d4a3865aa09f0774b65e5a48da8a7eff125c56136a701e758ceb5ef","libusb/AUTHORS":"27fd82b016838197d5276ba98d2187ed5a1ad0e171767322f356f0a9f6395941","libusb/COPYING":"5df07007198989c622f5d41de8d703e7bef3d0e79d62e24332ee739a452af62a","libusb/README":"ba152b9ae04b7cdf3cf09e8c91f60971b0c8f6106562e7cd31a2f2bf97f690db","libusb/README.md":"a6ef009aeac750d2bdd413c12fd3a596ecff40e1898d43b05ead0e5657d8d7ad","libusb/libusb/Makefile.am":"9fd9d63c9ecc678f3c4de2b40365ed5b79cce790184658c678335826466dad3d","libusb/libusb/Makefile.am.extra":"ce1da989a10a415739061fd4b9eaa3ff98f82cca59c27200800d9029ff697f34","libusb/libusb/Makefile.in":"cb56dd89300752b4c242925c6744fa663b04f3501bbc76a2d1e99e15cbb1749b","libusb/libusb/core.c":"33db091fdf75b690f9c5daf65ca8e3124978e897c2824d97fbc97567afea1d7b","libusb/libusb/descriptor.c":"4e80077f28a033ba07befceac0347a64ea0e23c34c93b96cfaf8b188d69e401d","libusb/libusb/hotplug.c":"7cf1e723dbae5427dd93fe8419d8efa3f3502d6def30a763b8c210e9780529fb","libusb/libusb/io.c":"c83a9e14b9ff8b9566e42d23d1e90c919e223fd1e04721c5ca1a3aff13239a3d","libusb/libusb/libusb-1.0.def":"4a07f2ea772f7f8bb3e117aa2c2141a79f1021afb4039be7588c1551fe9af66e","libusb/libusb/libusb-1.0.rc":"ca4851b3ca88161d9576bb2685588239cdaa4ba694013d50da6b8f5a602a18ab","libusb/libusb/libusb.h":"a61260ab145b051b86df2b0575956f01810190abe2c7df6aca831c33bdc8082c","libusb/libusb/libusbi.h":"cc234f9532988e492459b877cfd2e5e90704555a2a0a5310ee4add39627c7ed9","libusb/libusb/os/darwin_usb.c":"290b9913eba18e0f9f9fa0de75c59bec0daa4ccd02740a96fd8479d20da06557","libusb/libusb/os/darwin_usb.h":"fd26ccfdbf5fe02dc6f977e07e57a7cb3897bf9c88d19e60cc06f2b1fb12fcc1","libusb/libusb/os/emscripten_webusb.cpp":"dd8f91bbc3e1c75974c27a27fa2c162c285e3e3f5ef1a3c64018564656e758dd","libusb/libusb/os/events_posix.c":"aabddcc6ca86faba726ca87862eef9fc5d3b3168faef99c5be22827ac5b66ec0","libusb/libusb/os/events_posix.h":"f93e407eddc235375aace26fd17065e297cd963e60602002a6c100214a300b1b","libusb/libusb/os/events_windows.c":"726bdaeabb280c83681b88848d9a1526f73d3cd5f30b1aaf43dbd303e865148a","libusb/libusb/os/events_windows.h":"0690b9e46d32c02a9da65669128cabfa67f61dc6c234331c527a8eda4c98a1ea","libusb/libusb/os/haiku_pollfs.cpp":"4a9c603e1c44a00b6887720d88e5d71b08cc133954cef867243188829f7971cd","libusb/libusb/os/haiku_usb.h":"7b843867f3c5666a999326ca417b4ad2207114fcc593fc988d7706691aea8fb9","libusb/libusb/os/haiku_usb_backend.cpp":"fd10f1989f53002423fbab8041c6a20c5942f268f26ea80933a21a3d020f473d","libusb/libusb/os/haiku_usb_raw.cpp":"ebaee896cc0d2f0e196186811a6e42c27f8938b1fe4541a6acba40c715d0d537","libusb/libusb/os/haiku_usb_raw.h":"32c14b81a85481a983c6954e7d7fdd2ac0a4c67a4af23400b7af16afc5975aa3","libusb/libusb/os/linux_netlink.c":"6ad9b928501ad36dd9fffdbe713a9cdd9d4533b8324190bd060001528d8374f0","libusb/libusb/os/linux_udev.c":"0ca06ccb2341f008d09465b4142154a93240abd946d3298b4ced6c2ea9248115","libusb/libusb/os/linux_usbfs.c":"ac4401cec575cc2f12b71377cced5e5943e72366eedeffbe42e951134b26bd54","libusb/libusb/os/linux_usbfs.h":"a5deb9f3d62c1ec8fdeb9fa95717cb9d0717fdf1758aa2dad629e82906a3738e","libusb/libusb/os/netbsd_usb.c":"08ccca22489f1e102faca1ac934f23c347fba92a6b743796893950697f01115d","libusb/libusb/os/null_usb.c":"b41e75a6948803c3914e2b2012771ce75f2ae3aefe098a3c7c58c0c5e5a2c461","libusb/libusb/os/openbsd_usb.c":"1f084b5e83005a2c6942ae6ec6ab78cccd3a4eb5a63c778ff8adc5a155bf4b93","libusb/libusb/os/sunos_usb.c":"5a00462399b83501ba167dc49da622e903df28815fcfe2bd9c0704fe121a18c1","libusb/libusb/os/sunos_usb.h":"f1d312d8ba96af02a9688608b24d4abbdcc7c46f189cc700530179d5d5d68851","libusb/libusb/os/threads_posix.c":"779daa724575cd82c607563c9fe396b53d6f20c6a512444661e29ac30b82210a","libusb/libusb/os/threads_posix.h":"a85fbf402195885a8b0484b881b27ad56e22059655e86cf101d67b40fe1ff261","libusb/libusb/os/threads_windows.c":"33c5124b46509e59393bbc6da0f090325737a9666ae147d0776225c5b29c209b","libusb/libusb/os/threads_windows.h":"6bbf1b429a5abef2705e0c7ecb15daeea5e623f8dcb3312fc06e52a77a5e3a26","libusb/libusb/os/windows_common.c":"b1c26832c25d71bca4a50668f08a3728b26c6e2d398df4d5affb0da063b33e0f","libusb/libusb/os/windows_common.h":"01164ac769e7c90e5bfe0991cf40e0577b80ebd35ed73486994e6f8630fe052b","libusb/libusb/os/windows_hotplug.c":"28a145ee5ec8a08dc50a258efe2cd4701fbe27fbd1b2a44ce3f3b71a9a31e46b","libusb/libusb/os/windows_hotplug.h":"680936c93320a4c0bce4e344a621bade1133c8e13229ac2da48902717b54844e","libusb/libusb/os/windows_usbdk.c":"d3bdddb19b2a7a23dcd9c30c4abf6629f3cf7aa9e63d35bc3e92f5774e3fff90","libusb/libusb/os/windows_usbdk.h":"c31cd4bc3a7d11a08d2d377d29617222e0705ddd6ebff2851f5c936767f9f61a","libusb/libusb/os/windows_winusb.c":"1f6d600a0c579d2d7b13a86997e8fbe3ef47f2a5b0f014842e2143a4714f82fc","libusb/libusb/os/windows_winusb.h":"6d575e59083bbe0ebb585e35a2210c8ee65d7942786cf04028dcab68b17aa85c","libusb/libusb/strerror.c":"17ee9b6cf5b07b39adce9806339799a5085fde8c5f17ed86af324b856ca7d1ec","libusb/libusb/sync.c":"3bf6e788084956a3226933580f2cb2957c2de9e0a2ebeeec1a26e6c7529bfa68","libusb/libusb/version.h":"8ef05de838404213f63f3a27a073aee0e2bc072dc4415734772e756d34def7f8","libusb/libusb/version_nano.h":"243da8ad18f946e4ee896b296ac56834bf6b50c6211af1b62603483b60f58d50","libusb/msvc/Base.props":"cf766c4e86ded3ceacc12c16f55a04567435b141ebc1608fd1383ce6b31053f6","libusb/msvc/Configuration.Application.props":"13472acaad7abe373330b669cfb5dffad7410470ff82e5b8f2b5eac4c068ce47","libusb/msvc/Configuration.Base.props":"78eda7774ccbe21c1f9c890ce32227c4e8e54586fdbd3a82009d3974eb91f557","libusb/msvc/Configuration.DynamicLibrary.props":"6409cf0bdb1c9aa92b50dcb56e481b027851017d31b24de59c8bf80b53a38770","libusb/msvc/Configuration.StaticLibrary.props":"9961463e0c83308a285d680e4c7f4be6d85b137fc7bfe84df58f404ae19a3823","libusb/msvc/ProjectConfigurations.Base.props":"c33f8e7883fdf1dc258b3cafd2782210170e7a94d22c126779f38365c39b3069","libusb/msvc/build_all.ps1":"0d806172b88df5b57e921f45e8d91e50eca4e207af2a8e693353288a77466e01","libusb/msvc/config.h":"f529cf828f272763df2c5e61dc5d5f449d8248576915072f2ee47c9f8a07f46d","libusb/msvc/dpfp.vcxproj":"35927b20210f1f7465ea2edadc2165aabee6504b6c2e66956e862babf80b1663","libusb/msvc/dpfp_threaded.vcxproj":"c549d4401effd6eef5a51a9a35d39fb42e914b80edfc88342b13f54a1b117e27","libusb/msvc/fxload.vcxproj":"38a27c1876071ba3f14ee1828915cb7c80e4a66f339caace156e5c1f52cbb25f","libusb/msvc/getopt.vcxproj":"064c0f47aa91fdf287e2ad25c6f4e3412f24ce81cb9d9062bcdf6cf4228ad30c","libusb/msvc/getopt/bits/getopt_core.h":"238ff9f16662d515d6b191662965bb27e208b04dc929d04a69399de41bcd7d45","libusb/msvc/getopt/bits/getopt_ext.h":"100d4684d2bce41b0921e2dd10999548911c6b4797b875af51bb50f09f7deff7","libusb/msvc/getopt/features.h":"8bb9468fbb84a43ea28fa43be39865bfbb195db25caf95f5779d6fcb9c10f5d6","libusb/msvc/getopt/getopt.c":"c10c35d68e34ffab95265898f83d5f37c8280c2ee92360568cd318e0f1d7a0a5","libusb/msvc/getopt/getopt.h":"e1ebc0607ceeb5eae2eff2a413528d8ac3aa4de33e19d9c9571beb3143d90c3d","libusb/msvc/getopt/getopt1.c":"3135b9e7b001142f6276734200703fe8df595cb8c2510b547550be9ba29363c3","libusb/msvc/getopt/getopt_int.h":"8e112a94fdf937f02c8c7a4ebfb3263b6cd63174e403bda2ca497efab3a5d4d9","libusb/msvc/getopt/gettext.h":"fb60c648d091044ebb158fd16f65477fda4fd168b0395b27dbddaf8423770245","libusb/msvc/getopt/unistd.h":"ee245dcff04aa7803f7103e4b80f1b6517d1d959484cb06fc71fe3dd1490a0ff","libusb/msvc/hotplugtest.vcxproj":"8fd9634500360c2ef26b40da87f0963a3d34f041e75deaa88f5b47540f709aed","libusb/msvc/init_context.vcxproj":"c682626438dc0f5fc6b790e5fcb51b058b62bb90591d7040bb7f3911ed8fe3c6","libusb/msvc/libusb.sln":"d89534e7528f2871c72d995c2d75d8959c016ab81794950b4675f7e748db7e69","libusb/msvc/libusb_dll.vcxproj":"0b140ebb84c70ea271f182a7cddd6a582f06209eeeb04293b70db676bba162c0","libusb/msvc/libusb_static.vcxproj":"41b8afb72b42649f4778fa086ea7a63504817497a7f45c253176081a4788900f","libusb/msvc/listdevs.vcxproj":"5829569bc480582b8cfe8a856ce242c26c82af39e00e2c2b2339d3a34054fe0a","libusb/msvc/sam3u_benchmark.vcxproj":"c092b7e452b12a8f5aad545c8a5b4a438e43cea509a2ec672dd684a9b6315913","libusb/msvc/set_option.vcxproj":"349e1c6dbb7453b5f686e79bf9cfbab7eb5cded14abb1043812ebcef46e3615c","libusb/msvc/stress.vcxproj":"8630611fcd468a730bc1728ec49eeb3e3d6626d57374dbdb5770e3fd0a89d9d9","libusb/msvc/stress_mt.vcxproj":"2602c4466eee4dba192cce63de2ba625bcd5def770689e98ee1c93cc21ed4a4a","libusb/msvc/testlibusb.vcxproj":"0db63a651819bbe9ada7b391429f914841f21586426d1ee3cde74b65ed4fbe3c","libusb/msvc/xusb.vcxproj":"6dd6752cd658c33489610660e740054a2f9499f0df81cd16e9df0bc2f9835c5b","src/constants.rs":"6d4d764cdfbf6caee0893defa0798d3c87fadb78230a09bf3964acb21f6b304b","src/lib.rs":"973f673647f50f67c6e93a4b4e82b8f9613d4b6aed09c5203b55ab0ee6d9f5d9","tests/test.rs":"f38cdfa6f15c5854697422c0fb014c94b495f83955489a626a6a90e1751fe1bc"},"package":"da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f"} \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/.cargo_vcs_info.json b/rust_backend/vendor/libusb1-sys-0.7.0/.cargo_vcs_info.json new file mode 100644 index 00000000..78d47f8d --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "f4fc94e886d55969ea13c21ab0fc2ab5fa788ffc" + }, + "path_in_vcs": "libusb1-sys" +} \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/CHANGELOG.md b/rust_backend/vendor/libusb1-sys-0.7.0/CHANGELOG.md new file mode 100644 index 00000000..8f1acf69 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/CHANGELOG.md @@ -0,0 +1,54 @@ +# Changes + +## 0.7.0 + +* fix: Add missing fields to libusb_bos_descriptor and libusb_bos_dev_capability_descriptor [#161] +* Bump libusb to 1.0.27 [#201] +* Remove unneeded mut [#204] + +[#161]: https://github.com/a1ien/rusb/pull/161 +[#201]: https://github.com/a1ien/rusb/pull/201 +[#204]: https://github.com/a1ien/rusb/pull/204 + +## 0.6.5 +* Support pkg_config for MSVC. [#191] +* Fix package detection and build when cross-compiling from MSVC to GNU [#180] +* libusb_set_iso_packet_lengths panics on debug builds in newest nightly (2024-03-27) [#199] +* Added libusb_free_pollfds() in the available FFI methods. [#203] + +[#191]: https://github.com/a1ien/rusb/pull/191 +[#180]: https://github.com/a1ien/rusb/pull/180 +[#199]: https://github.com/a1ien/rusb/pull/199 +[#203]: https://github.com/a1ien/rusb/pull/203 + +## 0.6.3-0.6.4 +* Patch for macOS Big Sur and newer allowing to link statically [#133] +* Add libudev include paths as specified by pkg-config [#140] + +[#133]: https://github.com/a1ien/rusb/pull/133 +[#140]: https://github.com/a1ien/rusb/pull/140 + + +## 0.6.2 +* Rename compiled library when vendored libusb is used [#130] + +[#130]: https://github.com/a1ien/rusb/pull/130 + +## 0.6.1 +* Add LIBUSB_OPTION_NO_DEVICE_DISCOVERY constant +* Bump vendored libusb version from 1.0.24 to 1.0.25 [#119] + +[#119]: https://github.com/a1ien/rusb/pull/119 + +## 0.6.0 +* Allow null function pointers for libusb_set_log_cb() [#74] +* Allow null function pointers for libusb_set_pollfd_notifiers() [#71] +* Fix building of recent libusb on macOS [#108] +* Ignore vendored feature on FreeBSD [#109] +* Update definitions [#112] + +[#74]: https://github.com/a1ien/rusb/pull/74 +[#71]: https://github.com/a1ien/rusb/pull/71 +[#108]: https://github.com/a1ien/rusb/pull/108 +[#109]: https://github.com/a1ien/rusb/pull/109 +[#112]: https://github.com/a1ien/rusb/pull/112 diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.lock b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.lock new file mode 100644 index 00000000..7743a4bd --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.lock @@ -0,0 +1,37 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libusb1-sys" +version = "0.7.0" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml new file mode 100644 index 00000000..67ecedb7 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml @@ -0,0 +1,64 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "libusb1-sys" +version = "0.7.0" +authors = [ + "David Cuddeback ", + "Ilya Averyanov ", +] +build = "build.rs" +links = "usb-1.0" +include = [ + "/exaples", + "/libusb/libusb", + "/libusb/msvc", + "/src/*", + "/tests", + "build.rs", + "Cargo.toml", + "LICENSE", + "README.md", + "CHANGELOG.md", + "COPYING", + "AUTHORS", +] +description = "FFI bindings for libusb." +homepage = "https://github.com/a1ien/rusb" +readme = "README.md" +keywords = [ + "usb", + "libusb", + "hardware", + "bindings", +] +license = "MIT" +repository = "https://github.com/a1ien/rusb.git" + +[dependencies.libc] +version = "0.2" + +[build-dependencies.cc] +version = "1.0" + +[build-dependencies.pkg-config] +version = "0.3" + +[features] +vendored = [] + +[target."cfg(target_os = \"windows\")".build-dependencies.vcpkg] +version = "0.2" + +[badges.travis-ci] +repository = "a1ien/libusb1-sys" diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml.orig b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml.orig new file mode 100644 index 00000000..c4d4fb79 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/Cargo.toml.orig @@ -0,0 +1,47 @@ +[package] + +name = "libusb1-sys" +version = "0.7.0" +authors = ["David Cuddeback ", + "Ilya Averyanov "] +description = "FFI bindings for libusb." +license = "MIT" +homepage = "https://github.com/a1ien/rusb" +repository = "https://github.com/a1ien/rusb.git" +readme = "README.md" +keywords = ["usb", "libusb", "hardware", "bindings"] +edition = "2018" +links = "usb-1.0" # Required for metadata passing to work + +include = [ + "/exaples", + "/libusb/libusb", + "/libusb/msvc", + "/src/*", + "/tests", + "build.rs", + "Cargo.toml", + "LICENSE", + "README.md", + "CHANGELOG.md", + "COPYING", + "AUTHORS", +] + +build = "build.rs" + +[badges] +travis-ci = { repository = "a1ien/libusb1-sys" } + +[features] +vendored = [] + +[dependencies] +libc = "0.2" + +[target.'cfg(target_os = "windows")'.build-dependencies] +vcpkg = "0.2" + +[build-dependencies] +cc = "1.0" +pkg-config = "0.3" diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/LICENSE b/rust_backend/vendor/libusb1-sys-0.7.0/LICENSE new file mode 100644 index 00000000..cf8ea43e --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2015 David Cuddeback + 2019 Ilya Averyanov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/README.md b/rust_backend/vendor/libusb1-sys-0.7.0/README.md new file mode 100644 index 00000000..bc4f2c64 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/README.md @@ -0,0 +1,92 @@ +# Libusb Rust Bindings + +The `libusb1-sys` crate provides declarations and linkage for the `libusb` C library. Following the +`*-sys` package conventions, the `libusb1-sys` crate does not define higher-level abstractions over +the native `libusb` library functions. + +## Dependencies +In order to use the `libusb1-sys` crate, you must have the `libusb` library installed where it can be +found by `pkg-config`. + +All systems supported by `libusb` are also supported by the `libusb1-sys` crate. It's been tested on +Linux, OS X, and Windows. + +### Cross-Compiling +To link to a cross-compiled version of the native `libusb` library, it's necessary to set several +environment variables to configure `pkg-config` to work with a cross-compiler's sysroot. [Autotools +Mythbuster](https://autotools.io/) has a good explanation of [supporting +cross-compilation](https://autotools.io/pkgconfig/cross-compiling.html) with `pkg-config`. + +However, Rust's [`pkg-config` build helper](https://github.com/alexcrichton/pkg-config-rs) doesn't +support calling a `$CHOST`-prefixed `pkg-config`. It will always call `pkg-config` without a prefix. +To cross-compile `libusb1-sys` with the `pkg-config` build helper, one must define the environment +variables `PKG_CONFIG_DIR`, `PKG_CONFIG_LIBDIR`, and `PKG_CONFIG_SYSROOT_DIR` for the *default* +`pkg-config`. It's also necessary to set `PKG_CONFIG_ALLOW_CROSS` to tell Rust's `pkg-config` helper +that it's okay to proceed with a cross-compile. + +To adapt the `pkg-config` wrapper in the Autotools Mythbuster guide so that it works with Rust, one +will end up with a script similar to the following: + +```sh +#!/bin/sh + +SYSROOT=/build/root + +export PKG_CONFIG_DIR= +export PKG_CONFIG_LIBDIR=${SYSROOT}/usr/lib/pkgconfig:${SYSROOT}/usr/share/pkgconfig +export PKG_CONFIG_SYSROOT_DIR=${SYSROOT} +export PKG_CONFIG_ALLOW_CROSS=1 + +cargo build +``` + +## Usage +Add `libusb1-sys` as a dependency in `Cargo.toml`: + +```toml +[dependencies] +libusb1-sys = "0.6" +``` + +Import the `libusb1_sys` crate and use the functions as they're defined in the native `libusb` +library. See the [`libusb` 1.0 API documention](http://libusb.sourceforge.net/api-1.0/) for more +usage information. + +```rust +extern crate libusb1_sys as ffi; + +fn main() { + let version = unsafe { ffi::libusb_get_version() }; + + println!("libusb v{}.{}.{}.{}", version.major, version.minor, version.micro, version.nano); +} +``` + +### Native dependencies + +`libusb1-sys` exports [metadata] so that dependent crates can find the correct `libusb.h` header +and compile native code that depends on `libusb`. If a crate has a direct dependency on `libusb1-sys`, +its build script has access to the following environment variables: + +* `DEP_USB_1.0_INCLUDE` contains the include path with the correct `libusb.h` +* `DEP_USB_1.0_VENDORED` is set with a value of `1` if `libusb1-sys` compiled and linked to +its vendored copy of `libusb` +* `DEP_USB_1.0_STATIC` is set with a value of `1` if static linkage has been used instead of +dynamic. + +[metadata]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key + +### Finding Help +Since `libusb1-sys` is no more than a wrapper around the native `libusb` library, the best source for +help is the information already available for `libusb`: + +* [Home Page](http://libusb.info/) +* [API Documentation](http://libusb.sourceforge.net/api-1.0/) +* [Source](https://github.com/libusb/libusb) + + +## License +Distributed under the [MIT License](LICENSE). + +### License note. +If you link native `libusb` library statically then you must follow [GNU LGPL](https://github.com/libusb/libusb/blob/master/COPYING) from libusb. diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/build.rs b/rust_backend/vendor/libusb1-sys-0.7.0/build.rs new file mode 100644 index 00000000..891d6c4b --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/build.rs @@ -0,0 +1,224 @@ +use std::{env, fs, path::PathBuf}; + +static VERSION: &str = "1.0.30"; + +fn link(name: &str, bundled: bool) { + use std::env::var; + let target = var("TARGET").unwrap(); + let target: Vec<_> = target.split('-').collect(); + if target.get(2) == Some(&"windows") { + println!("cargo:rustc-link-lib=dylib={}", name); + if bundled && target.get(3) == Some(&"gnu") { + let dir = var("CARGO_MANIFEST_DIR").unwrap(); + println!("cargo:rustc-link-search=native={}/{}", dir, target[0]); + } + } +} + +pub fn link_framework(name: &str) { + println!("cargo:rustc-link-lib=framework={}", name); +} + +fn get_macos_major_version() -> Option { + if !cfg!(target_os = "macos") { + return None; + } + + let output = std::process::Command::new("sw_vers") + .args(["-productVersion"]) + .output() + .unwrap(); + let version = std::str::from_utf8(&output.stdout).unwrap().trim_end(); + let components: Vec<&str> = version.split('.').collect(); + let major: usize = components[0].parse().unwrap(); + Some(major) +} + +fn find_libusb_pkg(statik: bool) -> bool { + if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) { + #[cfg(target_os = "windows")] + return match vcpkg::Config::new().find_package("libusb") { + Ok(_) => true, + Err(e) => { + if pkg_config::probe_library("libusb-1.0").is_ok() { + true + } else { + println!("Can't find libusb pkg: {:?}", e); + false + } + } + }; + } + // https://github.com/rust-lang/rust/issues/96943 + let needs_rustc_issue_96943_workaround: bool = get_macos_major_version() + .map(|major| major >= 11) + .unwrap_or_default(); + + match pkg_config::Config::new().statik(statik).probe("libusb-1.0") { + Ok(l) => { + for lib in l.libs { + if statik { + if needs_rustc_issue_96943_workaround && lib == "objc" { + continue; + } + println!("cargo:rustc-link-lib=static={}", lib); + } + } + // Provide metadata and include directory for dependencies + if statik { + println!("cargo:static=1"); + } + assert!(l.include_paths.len() <= 1); // Cannot have multiple env vars with same name + for path in l.include_paths { + println!("cargo:include={}", path.to_str().unwrap()); + } + println!("cargo:version_number={}", l.version); + true + } + Err(e) => { + println!("Can't find libusb pkg: {:?}", e); + false + } + } +} + +fn make_source() { + let libusb_source = PathBuf::from("libusb"); + + /* + Example environment variables and values: + + CARGO_CFG_TARGET_ARCH: aarch64 + CARGO_CFG_TARGET_ENDIAN: little + CARGO_CFG_TARGET_ENV: + CARGO_CFG_TARGET_FAMILY: unix + CARGO_CFG_TARGET_OS: android + CARGO_CFG_TARGET_POINTER_WIDTH: 64 + CARGO_CFG_TARGET_VENDOR: unknown + CARGO_CFG_UNIX: + */ + + // Provide metadata and include directory for dependencies + println!("cargo:vendored=1"); + println!("cargo:static=1"); + let include_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("include"); + fs::create_dir_all(&include_dir).unwrap(); + fs::copy( + libusb_source.join("libusb/libusb.h"), + include_dir.join("libusb.h"), + ) + .unwrap(); + println!("cargo:include={}", include_dir.to_str().unwrap()); + + fs::File::create(format!("{}/{}", include_dir.display(), "config.h")).unwrap(); + let mut base_config = cc::Build::new(); + base_config.include(&include_dir); + base_config.include(libusb_source.join("libusb")); + + base_config.define("PRINTF_FORMAT(a, b)", Some("")); + base_config.define("ENABLE_LOGGING", Some("1")); + + if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) { + fs::copy( + libusb_source.join("msvc/config.h"), + include_dir.join("config.h"), + ) + .unwrap(); + } + + if std::env::var("CARGO_CFG_TARGET_OS") == Ok("macos".into()) { + base_config.define("OS_DARWIN", Some("1")); + base_config.define("TARGET_OS_OSX", Some("1")); + base_config.file(libusb_source.join("libusb/os/darwin_usb.c")); + link_framework("CoreFoundation"); + link_framework("IOKit"); + link_framework("Security"); + link("objc", false); + } + + if std::env::var("CARGO_CFG_TARGET_OS") == Ok("linux".into()) + || std::env::var("CARGO_CFG_TARGET_OS") == Ok("android".into()) + { + base_config.define("OS_LINUX", Some("1")); + base_config.define("HAVE_ASM_TYPES_H", Some("1")); + base_config.define("_GNU_SOURCE", Some("1")); + base_config.define("HAVE_TIMERFD", Some("1")); + base_config.define("HAVE_EVENTFD", Some("1")); + base_config.file(libusb_source.join("libusb/os/linux_netlink.c")); + base_config.file(libusb_source.join("libusb/os/linux_usbfs.c")); + } + + if std::env::var("CARGO_CFG_TARGET_FAMILY") == Ok("unix".into()) { + base_config.define("HAVE_SYS_TIME_H", Some("1")); + base_config.define("HAVE_NFDS_T", Some("1")); + base_config.define("PLATFORM_POSIX", Some("1")); + base_config.define("HAVE_CLOCK_GETTIME", Some("1")); + base_config.define( + "DEFAULT_VISIBILITY", + Some("__attribute__((visibility(\"default\")))"), + ); + + if let Ok(lib) = pkg_config::probe_library("libudev") { + base_config.define("USE_UDEV", Some("1")); + base_config.define("HAVE_LIBUDEV", Some("1")); + base_config.file(libusb_source.join("libusb/os/linux_udev.c")); + for path in lib.include_paths { + base_config.include(path.to_str().unwrap()); + } + }; + + println!("Including posix!"); + base_config.file(libusb_source.join("libusb/os/events_posix.c")); + base_config.file(libusb_source.join("libusb/os/threads_posix.c")); + } + + if std::env::var("CARGO_CFG_TARGET_OS") == Ok("windows".into()) { + if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) { + base_config.flag("/source-charset:utf-8"); + } + + base_config.warnings(false); + base_config.define("OS_WINDOWS", Some("1")); + base_config.file(libusb_source.join("libusb/os/events_windows.c")); + base_config.file(libusb_source.join("libusb/os/threads_windows.c")); + base_config.file(libusb_source.join("libusb/os/windows_common.c")); + base_config.file(libusb_source.join("libusb/os/windows_usbdk.c")); + base_config.file(libusb_source.join("libusb/os/windows_winusb.c")); + + base_config.define("DEFAULT_VISIBILITY", Some("")); + base_config.define("PLATFORM_WINDOWS", Some("1")); + link("user32", false); + } + + base_config.file(libusb_source.join("libusb/core.c")); + base_config.file(libusb_source.join("libusb/descriptor.c")); + base_config.file(libusb_source.join("libusb/hotplug.c")); + base_config.file(libusb_source.join("libusb/io.c")); + base_config.file(libusb_source.join("libusb/strerror.c")); + base_config.file(libusb_source.join("libusb/sync.c")); + + base_config.compile("usb-vendored"); + println!("cargo:version_number={}", VERSION); +} + +fn main() { + println!("cargo:rerun-if-env-changed=LIBUSB_STATIC"); + let statik = { + if cfg!(target_os = "macos") { + match std::env::var("LIBUSB_STATIC").unwrap_or_default().as_ref() { + "" | "0" => false, + _ => true, + } + } else { + std::env::var("CARGO_CFG_TARGET_FEATURE") + .map(|s| s.contains("crt-static")) + .unwrap_or_default() + } + }; + + let is_freebsd = std::env::var("CARGO_CFG_TARGET_OS") == Ok("freebsd".into()); + + if (!is_freebsd && cfg!(feature = "vendored")) || !find_libusb_pkg(statik) { + make_source(); + } +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/AUTHORS b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/AUTHORS new file mode 100644 index 00000000..c02bbd78 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/AUTHORS @@ -0,0 +1,245 @@ +Copyright © 2001 Johannes Erdfelt +Copyright © 2007-2009 Daniel Drake +Copyright © 2010-2012 Peter Stuge +Copyright © 2008-2016 Nathan Hjelm +Copyright © 2009-2013 Pete Batard +Copyright © 2009-2013 Ludovic Rousseau +Copyright © 2010-2012 Michael Plante +Copyright © 2011-2013 Hans de Goede +Copyright © 2012-2013 Martin Pieuchot +Copyright © 2012-2013 Toby Gray +Copyright © 2013-2018 Chris Dickens + +Other contributors: +Aaron Luft +Adam Korcz +Addison Crump +Adrian Bunk +Adrien Bertrand +Adrien Destugues +Akshay Jaggi +Alan Ott +Alan Stern +Aleksandr Mezin +Alexander Mot +Alexander Pyhalov +Alexander Schlarb +Alexander Stein +Alex Birkett +Alex Feinman +Alex Vatchenko +Andrew Aldridge +Andrew Fernandes +Andrew Goodney +Andy Chunyu +Andy McFadden +Angus Gratton +Anil Nair +Ankur Verma +Anthony Clay +Antoine Moreau +Antonio Ospite +Artem Egorkine +Aurelien Jarno +Axel Gembe +Aymeric Vincent +Baruch Siach +Bastien Nocera +Bei Zhang +Bence Csokas +Benjamin Berg +Benjamin Dobell +Bohdan Tymkiv +Brad Smith +Brent Rector +Bruno Harbulot +Carl Karsten +Christophe Zeitouny +Chris Zhu +Chunyu Xie +Colin Walters +Craig Hutchinson +Dave Camarillo +David Engraf +Davidlohr Bueso +David Moore +Dmitry Fleytman +Dmitry Kostjuchenko +Dmitry Zakablukov +Dominik Boehi +Doug Johnston +Edgar Fuß +Evan Hunter +Evan Miller +Fabien Sanglard +Fabrice Fontaine +Federico Manzan +Felipe Balbi +Florian Albrechtskirchinger +Francesco Montorsi +Francisco Facioni +Francis Hart +Frank Li +Frederik Carlier +Freek Dijkstra +Gaurav Gupta +Graeme Gill +Greg Kroah-Hartman +Gustavo Zacarias +Haidong Zheng +Hannes Franke +Hans Ulrich Niedermann +Harry Mallon +Hartwig Wiesmann +Hector Martin +Hoi-Ho Chan +Ido Yariv +Igor Anokhin +Ihor Dutchak +Ilya Konstantinov +Ingvar Stepanyan +Jakub Klama +James Hanko +Jeffrey Nichols +Jesse Taube +Jie Zhang +Jim Chen +Johann Richard +John Keeping +John Sheu +Jon Beniston +Jonas Malaco +Jonathon Jongsma +Joost Muller +Josh Gao +Joshua Blake +Joshua Hou +Joshua M. Clulow +Juan Cruz Viotti +Julian Scheel +Justin Bischoff +Karsten Koenig +Keith Ahluwalia +Kenjiro Tsuji +Kimura Masaru +Konrad Rzepecki +Kuangye Guo +Lars Kanis +Lars Wirzenius +Lei Chen +Léo Lam +Liang Yunwang +Logan Gallois +Lonnie Abelbeck +Luca Longinotti +Luz Paz +Łukasz Bartosik +Mac Wang +Marco Trevisan (Treviño) +Marcus Meissner +Mario Kleiner +Mark Kuo +Mark Lee +Markus Heidelberg +Martin Ettl +Martin Koegler +Martin Ling +Martin Thierer +Mathias Hjärtström +Matt Liberty +Matthew Stapleton +Matthias Bolte +Matthijs Lavrijsen +Michael Dickens +Michel Zou +Mike Frysinger +Mikhail Gusarov +Mikolaj Kucharski +Morgan Leborgne +Moritz Fischer +Nancy Li +Nia Alarie +Nicholas Corgan +Nicolas Bats +Niklas Gürtler +Ole André Vadla Ravnås +Oleksand Radovenchyk +Omri Iluz +Orhan aib Kavrakoglu +Orin Eman +Ozkan Sezer +Pablo Prietz +Patrick Stewart +Paul Cercueil +Paul Fertser +Paul Qureshi +Pekka Nikander +Petr Pazourek +Philémon Favrod +Pino Toscano +Radu Vele +Rob Walker +Romain Vimont +Roman Kalashnikov +Rosen Penev +Ryan Hileman +Ryan Metcalfe +Ryan Schmidt +Saleem Rashid +Sameeh Jubran +Sean McBride +Sebastian Pipping +Sebastian von Ohr +Sergey Serb +Shawn Hoffman +Simon Chan +Simon Haggett +Simon Newton +Slash Gordon +Stefan Agner +Stefan Tauner +Steinar H. Gunderson +Stephen Groat +Sylvain Fasel +Theo Buehler +Theodor Engøy +Thomas Röfer +Tim Hutt +Tim Roberts +Tobias Klauser +Toby Peterson +Tormod Volden +Trygve Laugstøl +Uri Lublin +Uwe Bonnes +Vadim Mikhailov +Vasily Khoruzhick +Vegard Storheil Eriksen +Venkatesh Shukla +Vianney le Clément de Saint-Marcq +Victor Toso +Vinicius Tinti +Vitali Lovich +Vladimir Beloborodov +Vladimir Gladkov +William Orr +William Skellenger +Xiaofan Chen +Yegor Yefremov +Yiwei Lin +Zeng Guang +Zhiqiang Liu +Zoltán Kovács +Сергей Валерьевич +Ларионов Даниил +Роман Донченко +jonner +orbitcowboy +osy +parafin +RipleyTom +Seneral +saur0n +SomeAlphabetGuy +winterrace +xloem diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/COPYING b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/COPYING new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README new file mode 100644 index 00000000..6736bee6 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README @@ -0,0 +1,32 @@ +# libusb + +libusb is a library for USB device access from Linux, macOS, +Windows, OpenBSD/NetBSD, Haiku, Solaris userspace, and WebAssembly +via WebUSB. +It is written in C (Haiku backend in C++) and licensed under the GNU +Lesser General Public License version 2.1 or, at your option, any later +version (see COPYING). + +libusb is abstracted internally in such a way that it can hopefully +be ported to other operating systems. Please see the PORTING +file for more information. + +libusb homepage: +https://libusb.info/ + +Developers will wish to consult the API documentation: +http://api.libusb.info + +Use the mailing list for questions, comments, etc: +http://mailing-list.libusb.info + +- Tormod Volden +- Sylvain Fasel +- Sean McBride +- Nathan Hjelm +- Xiaofan Chen +- Hans de Goede +- Ludovic Rousseau +- Chris Dickens + +(Please use the mailing list rather than mailing developers directly) diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README.md b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README.md new file mode 100644 index 00000000..c25266a2 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/README.md @@ -0,0 +1,33 @@ +# libusb + +[![Build Status](https://travis-ci.org/libusb/libusb.svg?branch=master)](https://travis-ci.org/libusb/libusb) +[![Build Status](https://ci.appveyor.com/api/projects/status/xvrfam94jii4a6lw?svg=true)](https://ci.appveyor.com/project/LudovicRousseau/libusb) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/2180/badge.svg)](https://scan.coverity.com/projects/libusb-libusb) + +libusb is a library for USB device access from Linux, macOS, +Windows, OpenBSD/NetBSD, Haiku, Solaris userspace, and WebAssembly +via WebUSB. +It is written in C (Haiku backend in C++) and licensed under the GNU +Lesser General Public License version 2.1 or, at your option, any later +version (see [COPYING](COPYING)). + +libusb is abstracted internally in such a way that it can hopefully +be ported to other operating systems. Please see the [PORTING](PORTING) +file for more information. + +libusb homepage: +https://libusb.info/ + +Developers will wish to consult the API documentation: +http://api.libusb.info + +Use the mailing list for questions, comments, etc: +http://mailing-list.libusb.info + +- Hans de Goede +- Xiaofan Chen +- Ludovic Rousseau +- Nathan Hjelm +- Chris Dickens + +(Please use the mailing list rather than mailing developers directly) diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am new file mode 100644 index 00000000..2b972960 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am @@ -0,0 +1,102 @@ +AUTOMAKE_OPTIONS = subdir-objects + +AM_CFLAGS += -fvisibility=hidden $(THREAD_CFLAGS) +AM_CXXFLAGS += -fvisibility=hidden $(THREAD_CFLAGS) + +lib_LTLIBRARIES = libusb-1.0.la + +POSIX_PLATFORM_SRC = os/events_posix.h os/events_posix.c \ + os/threads_posix.h os/threads_posix.c +WINDOWS_PLATFORM_SRC = os/events_windows.h os/events_windows.c \ + os/threads_windows.h os/threads_windows.c + +if PLATFORM_POSIX +PLATFORM_SRC = $(POSIX_PLATFORM_SRC) +else +PLATFORM_SRC = $(WINDOWS_PLATFORM_SRC) +endif + +OS_DARWIN_SRC = os/darwin_usb.h os/darwin_usb.c +OS_HAIKU_SRC = os/haiku_usb.h os/haiku_usb_backend.cpp \ + os/haiku_pollfs.cpp os/haiku_usb_raw.h os/haiku_usb_raw.cpp +OS_LINUX_SRC = os/linux_usbfs.h os/linux_usbfs.c +OS_EMSCRIPTEN_SRC = os/emscripten_webusb.cpp +OS_NETBSD_SRC = os/netbsd_usb.c +OS_NULL_SRC = os/null_usb.c +OS_OPENBSD_SRC = os/openbsd_usb.c +OS_SUNOS_SRC = os/sunos_usb.h os/sunos_usb.c +OS_WINDOWS_SRC = libusb-1.0.def libusb-1.0.rc \ + os/windows_common.h os/windows_common.c \ + os/windows_usbdk.h os/windows_usbdk.c \ + os/windows_winusb.h os/windows_winusb.c + +if LIBUSB_WINDOWS_HOTPLUG +OS_WINDOWS_SRC += os/windows_hotplug.h os/windows_hotplug.c +endif + +if OS_DARWIN +OS_SRC = $(OS_DARWIN_SRC) +endif + +noinst_LTLIBRARIES = + +if OS_HAIKU +noinst_LTLIBRARIES += libusb_haiku.la +libusb_haiku_la_SOURCES = $(OS_HAIKU_SRC) +libusb_1_0_la_LIBADD = libusb_haiku.la +endif + +if OS_LINUX +OS_SRC = $(OS_LINUX_SRC) +if USE_UDEV +OS_SRC += os/linux_udev.c +else +OS_SRC += os/linux_netlink.c +endif +endif + +if OS_EMSCRIPTEN +noinst_LTLIBRARIES += libusb_emscripten.la +libusb_emscripten_la_SOURCES = $(OS_EMSCRIPTEN_SRC) +AM_CXXFLAGS += -std=c++20 +libusb_1_0_la_LIBADD = libusb_emscripten.la +endif + +if OS_NETBSD +OS_SRC = $(OS_NETBSD_SRC) +endif + +if OS_NULL +OS_SRC = $(OS_NULL_SRC) +endif + +if OS_OPENBSD +OS_SRC = $(OS_OPENBSD_SRC) +endif + +if OS_SUNOS +OS_SRC = $(OS_SUNOS_SRC) +endif + +if OS_WINDOWS +OS_SRC = $(OS_WINDOWS_SRC) + +include Makefile.am.extra + +# Dependencies for compiling libusb-1.0.lo from libusb-1.0.rc +-include ./$(DEPDIR)/libusb-1.0.Plo + +if CREATE_IMPORT_LIB +all-local: .libs/libusb-1.0.dll.a +# Rebuild the import lib from the .def so that MS and MinGW DLLs can be interchanged +.libs/libusb-1.0.dll.a: libusb-1.0.def libusb-1.0.la + $(AM_V_DLLTOOL)$(DLLTOOL) $(DLLTOOLFLAGS) --kill-at --input-def $< --dllname libusb-1.0.dll --output-lib $@ +endif +endif + +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.c io.c strerror.c sync.c \ + $(PLATFORM_SRC) $(OS_SRC) + +pkginclude_HEADERS = libusb.h diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am.extra b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am.extra new file mode 100644 index 00000000..a5c14905 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.am.extra @@ -0,0 +1,26 @@ +AM_V_DLLTOOL = $(am__v_DLLTOOL_$(V)) +am__v_DLLTOOL_ = $(am__v_DLLTOOL_$(AM_DEFAULT_VERBOSITY)) +am__v_DLLTOOL_0 = @echo " DLLTOOL " $@; +am__v_DLLTOOL_1 = + +AM_V_RC = $(am__v_RC_$(V)) +am__v_RC_ = $(am__v_RC_$(AM_DEFAULT_VERBOSITY)) +am__v_RC_0 = @echo " RC " $@; +am__v_RC_1 = + +LTRC = $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(RC) $(AM_RCFLAGS) \ + $(RCFLAGS) + +RCPPARGS = \ + --preprocessor-arg -MT \ + --preprocessor-arg $@ \ + --preprocessor-arg -MD \ + --preprocessor-arg -MP \ + --preprocessor-arg -MF \ + --preprocessor-arg $$depbase.Tpo + +.rc.lo: + $(AM_V_RC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ + $(LTRC) $(RCPPARGS) -i $< -o $@ &&\ + $(am__mv) $$depbase.Tpo $$depbase.Plo diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.in b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.in new file mode 100644 index 00000000..6bef64b7 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/Makefile.in @@ -0,0 +1,1030 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@LIBUSB_WINDOWS_HOTPLUG_TRUE@am__append_1 = os/windows_hotplug.h os/windows_hotplug.c +@OS_HAIKU_TRUE@am__append_2 = libusb_haiku.la +@OS_LINUX_TRUE@@USE_UDEV_TRUE@am__append_3 = os/linux_udev.c +@OS_LINUX_TRUE@@USE_UDEV_FALSE@am__append_4 = os/linux_netlink.c +@OS_EMSCRIPTEN_TRUE@am__append_5 = libusb_emscripten.la +@OS_EMSCRIPTEN_TRUE@am__append_6 = -std=c++20 +subdir = libusb +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/libusb/version.h $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +@OS_EMSCRIPTEN_FALSE@@OS_HAIKU_TRUE@libusb_1_0_la_DEPENDENCIES = \ +@OS_EMSCRIPTEN_FALSE@@OS_HAIKU_TRUE@ libusb_haiku.la +@OS_EMSCRIPTEN_TRUE@libusb_1_0_la_DEPENDENCIES = libusb_emscripten.la +am__libusb_1_0_la_SOURCES_DIST = libusbi.h version.h version_nano.h \ + core.c descriptor.c hotplug.c io.c strerror.c sync.c \ + os/events_windows.h os/events_windows.c os/threads_windows.h \ + os/threads_windows.c os/events_posix.h os/events_posix.c \ + os/threads_posix.h os/threads_posix.c os/darwin_usb.h \ + os/darwin_usb.c os/linux_udev.c os/linux_netlink.c \ + os/linux_usbfs.h os/linux_usbfs.c os/netbsd_usb.c \ + os/null_usb.c os/openbsd_usb.c os/sunos_usb.h os/sunos_usb.c \ + libusb-1.0.def libusb-1.0.rc os/windows_common.h \ + os/windows_common.c os/windows_usbdk.h os/windows_usbdk.c \ + os/windows_winusb.h os/windows_winusb.c os/windows_hotplug.h \ + os/windows_hotplug.c +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = os/events_windows.lo os/threads_windows.lo +am__objects_2 = os/events_posix.lo os/threads_posix.lo +@PLATFORM_POSIX_FALSE@am__objects_3 = $(am__objects_1) +@PLATFORM_POSIX_TRUE@am__objects_3 = $(am__objects_2) +am__objects_4 = os/darwin_usb.lo +@OS_LINUX_TRUE@@USE_UDEV_TRUE@am__objects_5 = os/linux_udev.lo +@OS_LINUX_TRUE@@USE_UDEV_FALSE@am__objects_6 = os/linux_netlink.lo +am__objects_7 = os/linux_usbfs.lo +am__objects_8 = os/netbsd_usb.lo +am__objects_9 = os/null_usb.lo +am__objects_10 = os/openbsd_usb.lo +am__objects_11 = os/sunos_usb.lo +@LIBUSB_WINDOWS_HOTPLUG_TRUE@am__objects_12 = os/windows_hotplug.lo +am__objects_13 = libusb-1.0.lo os/windows_common.lo \ + os/windows_usbdk.lo os/windows_winusb.lo $(am__objects_12) +@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_NULL_FALSE@@OS_OPENBSD_FALSE@@OS_SUNOS_FALSE@@OS_WINDOWS_TRUE@am__objects_14 = $(am__objects_13) +@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_NULL_FALSE@@OS_OPENBSD_FALSE@@OS_SUNOS_TRUE@am__objects_14 = $(am__objects_11) +@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_NULL_FALSE@@OS_OPENBSD_TRUE@am__objects_14 = $(am__objects_10) +@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_NULL_TRUE@am__objects_14 = $(am__objects_9) +@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_TRUE@am__objects_14 = $(am__objects_8) +@OS_DARWIN_FALSE@@OS_LINUX_TRUE@am__objects_14 = $(am__objects_7) \ +@OS_DARWIN_FALSE@@OS_LINUX_TRUE@ $(am__objects_5) \ +@OS_DARWIN_FALSE@@OS_LINUX_TRUE@ $(am__objects_6) +@OS_DARWIN_TRUE@am__objects_14 = $(am__objects_4) $(am__objects_5) \ +@OS_DARWIN_TRUE@ $(am__objects_6) +am_libusb_1_0_la_OBJECTS = core.lo descriptor.lo hotplug.lo io.lo \ + strerror.lo sync.lo $(am__objects_3) $(am__objects_14) +libusb_1_0_la_OBJECTS = $(am_libusb_1_0_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libusb_1_0_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libusb_1_0_la_LDFLAGS) $(LDFLAGS) -o $@ +libusb_emscripten_la_LIBADD = +am__libusb_emscripten_la_SOURCES_DIST = os/emscripten_webusb.cpp +am__objects_15 = os/emscripten_webusb.lo +@OS_EMSCRIPTEN_TRUE@am_libusb_emscripten_la_OBJECTS = \ +@OS_EMSCRIPTEN_TRUE@ $(am__objects_15) +libusb_emscripten_la_OBJECTS = $(am_libusb_emscripten_la_OBJECTS) +@OS_EMSCRIPTEN_TRUE@am_libusb_emscripten_la_rpath = +libusb_haiku_la_LIBADD = +am__libusb_haiku_la_SOURCES_DIST = os/haiku_usb.h \ + os/haiku_usb_backend.cpp os/haiku_pollfs.cpp \ + os/haiku_usb_raw.h os/haiku_usb_raw.cpp +am__objects_16 = os/haiku_usb_backend.lo os/haiku_pollfs.lo \ + os/haiku_usb_raw.lo +@OS_HAIKU_TRUE@am_libusb_haiku_la_OBJECTS = $(am__objects_16) +libusb_haiku_la_OBJECTS = $(am_libusb_haiku_la_OBJECTS) +@OS_HAIKU_TRUE@am_libusb_haiku_la_rpath = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/core.Plo ./$(DEPDIR)/descriptor.Plo \ + ./$(DEPDIR)/hotplug.Plo ./$(DEPDIR)/io.Plo \ + ./$(DEPDIR)/strerror.Plo ./$(DEPDIR)/sync.Plo \ + os/$(DEPDIR)/darwin_usb.Plo os/$(DEPDIR)/emscripten_webusb.Plo \ + os/$(DEPDIR)/events_posix.Plo os/$(DEPDIR)/events_windows.Plo \ + os/$(DEPDIR)/haiku_pollfs.Plo \ + os/$(DEPDIR)/haiku_usb_backend.Plo \ + os/$(DEPDIR)/haiku_usb_raw.Plo os/$(DEPDIR)/linux_netlink.Plo \ + os/$(DEPDIR)/linux_udev.Plo os/$(DEPDIR)/linux_usbfs.Plo \ + os/$(DEPDIR)/netbsd_usb.Plo os/$(DEPDIR)/null_usb.Plo \ + os/$(DEPDIR)/openbsd_usb.Plo os/$(DEPDIR)/sunos_usb.Plo \ + os/$(DEPDIR)/threads_posix.Plo \ + os/$(DEPDIR)/threads_windows.Plo \ + os/$(DEPDIR)/windows_common.Plo \ + os/$(DEPDIR)/windows_hotplug.Plo \ + os/$(DEPDIR)/windows_usbdk.Plo os/$(DEPDIR)/windows_winusb.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libusb_1_0_la_SOURCES) $(libusb_emscripten_la_SOURCES) \ + $(libusb_haiku_la_SOURCES) +DIST_SOURCES = $(am__libusb_1_0_la_SOURCES_DIST) \ + $(am__libusb_emscripten_la_SOURCES_DIST) \ + $(am__libusb_haiku_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.am.extra $(srcdir)/Makefile.in \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_CFLAGS = @AM_CFLAGS@ -fvisibility=hidden $(THREAD_CFLAGS) +AM_CPPFLAGS = @AM_CPPFLAGS@ +AM_CXXFLAGS = @AM_CXXFLAGS@ -fvisibility=hidden $(THREAD_CFLAGS) \ + $(am__append_6) +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_LDFLAGS = @AM_LDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN_HTMLDIR = @DOXYGEN_HTMLDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_LDFLAGS = @LT_LDFLAGS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RC = @RC@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +THREAD_CFLAGS = @THREAD_CFLAGS@ +THREAD_LIBS = @THREAD_LIBS@ +UMOCKDEV_CFLAGS = @UMOCKDEV_CFLAGS@ +UMOCKDEV_HOTPLUG_CFLAGS = @UMOCKDEV_HOTPLUG_CFLAGS@ +UMOCKDEV_HOTPLUG_LIBS = @UMOCKDEV_HOTPLUG_LIBS@ +UMOCKDEV_LIBS = @UMOCKDEV_LIBS@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = subdir-objects +lib_LTLIBRARIES = libusb-1.0.la +POSIX_PLATFORM_SRC = os/events_posix.h os/events_posix.c \ + os/threads_posix.h os/threads_posix.c + +WINDOWS_PLATFORM_SRC = os/events_windows.h os/events_windows.c \ + os/threads_windows.h os/threads_windows.c + +@PLATFORM_POSIX_FALSE@PLATFORM_SRC = $(WINDOWS_PLATFORM_SRC) +@PLATFORM_POSIX_TRUE@PLATFORM_SRC = $(POSIX_PLATFORM_SRC) +OS_DARWIN_SRC = os/darwin_usb.h os/darwin_usb.c +OS_HAIKU_SRC = os/haiku_usb.h os/haiku_usb_backend.cpp \ + os/haiku_pollfs.cpp os/haiku_usb_raw.h os/haiku_usb_raw.cpp + +OS_LINUX_SRC = os/linux_usbfs.h os/linux_usbfs.c +OS_EMSCRIPTEN_SRC = os/emscripten_webusb.cpp +OS_NETBSD_SRC = os/netbsd_usb.c +OS_NULL_SRC = os/null_usb.c +OS_OPENBSD_SRC = os/openbsd_usb.c +OS_SUNOS_SRC = os/sunos_usb.h os/sunos_usb.c +OS_WINDOWS_SRC = libusb-1.0.def libusb-1.0.rc os/windows_common.h \ + os/windows_common.c os/windows_usbdk.h os/windows_usbdk.c \ + os/windows_winusb.h os/windows_winusb.c $(am__append_1) +@OS_DARWIN_TRUE@OS_SRC = $(OS_DARWIN_SRC) $(am__append_3) \ +@OS_DARWIN_TRUE@ $(am__append_4) +@OS_LINUX_TRUE@OS_SRC = $(OS_LINUX_SRC) $(am__append_3) \ +@OS_LINUX_TRUE@ $(am__append_4) +@OS_NETBSD_TRUE@OS_SRC = $(OS_NETBSD_SRC) +@OS_NULL_TRUE@OS_SRC = $(OS_NULL_SRC) +@OS_OPENBSD_TRUE@OS_SRC = $(OS_OPENBSD_SRC) +@OS_SUNOS_TRUE@OS_SRC = $(OS_SUNOS_SRC) +@OS_WINDOWS_TRUE@OS_SRC = $(OS_WINDOWS_SRC) +noinst_LTLIBRARIES = $(am__append_2) $(am__append_5) +@OS_HAIKU_TRUE@libusb_haiku_la_SOURCES = $(OS_HAIKU_SRC) +@OS_EMSCRIPTEN_TRUE@libusb_1_0_la_LIBADD = libusb_emscripten.la +@OS_HAIKU_TRUE@libusb_1_0_la_LIBADD = libusb_haiku.la +@OS_EMSCRIPTEN_TRUE@libusb_emscripten_la_SOURCES = $(OS_EMSCRIPTEN_SRC) +@OS_WINDOWS_TRUE@AM_V_DLLTOOL = $(am__v_DLLTOOL_$(V)) +@OS_WINDOWS_TRUE@am__v_DLLTOOL_ = $(am__v_DLLTOOL_$(AM_DEFAULT_VERBOSITY)) +@OS_WINDOWS_TRUE@am__v_DLLTOOL_0 = @echo " DLLTOOL " $@; +@OS_WINDOWS_TRUE@am__v_DLLTOOL_1 = +@OS_WINDOWS_TRUE@AM_V_RC = $(am__v_RC_$(V)) +@OS_WINDOWS_TRUE@am__v_RC_ = $(am__v_RC_$(AM_DEFAULT_VERBOSITY)) +@OS_WINDOWS_TRUE@am__v_RC_0 = @echo " RC " $@; +@OS_WINDOWS_TRUE@am__v_RC_1 = +@OS_WINDOWS_TRUE@LTRC = $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) \ +@OS_WINDOWS_TRUE@ $(LIBTOOLFLAGS) --mode=compile $(RC) $(AM_RCFLAGS) \ +@OS_WINDOWS_TRUE@ $(RCFLAGS) + +@OS_WINDOWS_TRUE@RCPPARGS = \ +@OS_WINDOWS_TRUE@ --preprocessor-arg -MT \ +@OS_WINDOWS_TRUE@ --preprocessor-arg $@ \ +@OS_WINDOWS_TRUE@ --preprocessor-arg -MD \ +@OS_WINDOWS_TRUE@ --preprocessor-arg -MP \ +@OS_WINDOWS_TRUE@ --preprocessor-arg -MF \ +@OS_WINDOWS_TRUE@ --preprocessor-arg $$depbase.Tpo + +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.c io.c strerror.c sync.c \ + $(PLATFORM_SRC) $(OS_SRC) + +pkginclude_HEADERS = libusb.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj .rc +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Makefile.am.extra $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libusb/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libusb/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(srcdir)/Makefile.am.extra $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +os/$(am__dirstamp): + @$(MKDIR_P) os + @: > os/$(am__dirstamp) +os/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) os/$(DEPDIR) + @: > os/$(DEPDIR)/$(am__dirstamp) +os/events_windows.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/threads_windows.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/events_posix.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/threads_posix.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/darwin_usb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/linux_udev.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/linux_netlink.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/linux_usbfs.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/netbsd_usb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/null_usb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/openbsd_usb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/sunos_usb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/windows_common.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/windows_usbdk.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/windows_winusb.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/windows_hotplug.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) + +libusb-1.0.la: $(libusb_1_0_la_OBJECTS) $(libusb_1_0_la_DEPENDENCIES) $(EXTRA_libusb_1_0_la_DEPENDENCIES) + $(AM_V_CCLD)$(libusb_1_0_la_LINK) -rpath $(libdir) $(libusb_1_0_la_OBJECTS) $(libusb_1_0_la_LIBADD) $(LIBS) +os/emscripten_webusb.lo: os/$(am__dirstamp) \ + os/$(DEPDIR)/$(am__dirstamp) + +libusb_emscripten.la: $(libusb_emscripten_la_OBJECTS) $(libusb_emscripten_la_DEPENDENCIES) $(EXTRA_libusb_emscripten_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(am_libusb_emscripten_la_rpath) $(libusb_emscripten_la_OBJECTS) $(libusb_emscripten_la_LIBADD) $(LIBS) +os/haiku_usb_backend.lo: os/$(am__dirstamp) \ + os/$(DEPDIR)/$(am__dirstamp) +os/haiku_pollfs.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) +os/haiku_usb_raw.lo: os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp) + +libusb_haiku.la: $(libusb_haiku_la_OBJECTS) $(libusb_haiku_la_DEPENDENCIES) $(EXTRA_libusb_haiku_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(am_libusb_haiku_la_rpath) $(libusb_haiku_la_OBJECTS) $(libusb_haiku_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f os/*.$(OBJEXT) + -rm -f os/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hotplug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sync.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/darwin_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/emscripten_webusb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/events_posix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/events_windows.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/haiku_pollfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/haiku_usb_backend.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/haiku_usb_raw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/linux_netlink.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/linux_udev.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/linux_usbfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/netbsd_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/null_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/openbsd_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/sunos_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/threads_posix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/threads_windows.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/windows_common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/windows_hotplug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/windows_usbdk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/windows_winusb.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf os/.libs os/_libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@CREATE_IMPORT_LIB_FALSE@all-local: +@OS_WINDOWS_FALSE@all-local: +all-am: Makefile $(LTLIBRARIES) $(HEADERS) all-local +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f os/$(DEPDIR)/$(am__dirstamp) + -rm -f os/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/core.Plo + -rm -f ./$(DEPDIR)/descriptor.Plo + -rm -f ./$(DEPDIR)/hotplug.Plo + -rm -f ./$(DEPDIR)/io.Plo + -rm -f ./$(DEPDIR)/strerror.Plo + -rm -f ./$(DEPDIR)/sync.Plo + -rm -f os/$(DEPDIR)/darwin_usb.Plo + -rm -f os/$(DEPDIR)/emscripten_webusb.Plo + -rm -f os/$(DEPDIR)/events_posix.Plo + -rm -f os/$(DEPDIR)/events_windows.Plo + -rm -f os/$(DEPDIR)/haiku_pollfs.Plo + -rm -f os/$(DEPDIR)/haiku_usb_backend.Plo + -rm -f os/$(DEPDIR)/haiku_usb_raw.Plo + -rm -f os/$(DEPDIR)/linux_netlink.Plo + -rm -f os/$(DEPDIR)/linux_udev.Plo + -rm -f os/$(DEPDIR)/linux_usbfs.Plo + -rm -f os/$(DEPDIR)/netbsd_usb.Plo + -rm -f os/$(DEPDIR)/null_usb.Plo + -rm -f os/$(DEPDIR)/openbsd_usb.Plo + -rm -f os/$(DEPDIR)/sunos_usb.Plo + -rm -f os/$(DEPDIR)/threads_posix.Plo + -rm -f os/$(DEPDIR)/threads_windows.Plo + -rm -f os/$(DEPDIR)/windows_common.Plo + -rm -f os/$(DEPDIR)/windows_hotplug.Plo + -rm -f os/$(DEPDIR)/windows_usbdk.Plo + -rm -f os/$(DEPDIR)/windows_winusb.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/core.Plo + -rm -f ./$(DEPDIR)/descriptor.Plo + -rm -f ./$(DEPDIR)/hotplug.Plo + -rm -f ./$(DEPDIR)/io.Plo + -rm -f ./$(DEPDIR)/strerror.Plo + -rm -f ./$(DEPDIR)/sync.Plo + -rm -f os/$(DEPDIR)/darwin_usb.Plo + -rm -f os/$(DEPDIR)/emscripten_webusb.Plo + -rm -f os/$(DEPDIR)/events_posix.Plo + -rm -f os/$(DEPDIR)/events_windows.Plo + -rm -f os/$(DEPDIR)/haiku_pollfs.Plo + -rm -f os/$(DEPDIR)/haiku_usb_backend.Plo + -rm -f os/$(DEPDIR)/haiku_usb_raw.Plo + -rm -f os/$(DEPDIR)/linux_netlink.Plo + -rm -f os/$(DEPDIR)/linux_udev.Plo + -rm -f os/$(DEPDIR)/linux_usbfs.Plo + -rm -f os/$(DEPDIR)/netbsd_usb.Plo + -rm -f os/$(DEPDIR)/null_usb.Plo + -rm -f os/$(DEPDIR)/openbsd_usb.Plo + -rm -f os/$(DEPDIR)/sunos_usb.Plo + -rm -f os/$(DEPDIR)/threads_posix.Plo + -rm -f os/$(DEPDIR)/threads_windows.Plo + -rm -f os/$(DEPDIR)/windows_common.Plo + -rm -f os/$(DEPDIR)/windows_hotplug.Plo + -rm -f os/$(DEPDIR)/windows_usbdk.Plo + -rm -f os/$(DEPDIR)/windows_winusb.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ + check-am clean clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgincludeHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-libLTLIBRARIES \ + uninstall-pkgincludeHEADERS + +.PRECIOUS: Makefile + + +@OS_WINDOWS_TRUE@.rc.lo: +@OS_WINDOWS_TRUE@ $(AM_V_RC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@OS_WINDOWS_TRUE@ $(LTRC) $(RCPPARGS) -i $< -o $@ &&\ +@OS_WINDOWS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo + +# Dependencies for compiling libusb-1.0.lo from libusb-1.0.rc +@OS_WINDOWS_TRUE@-include ./$(DEPDIR)/libusb-1.0.Plo + +@CREATE_IMPORT_LIB_TRUE@@OS_WINDOWS_TRUE@all-local: .libs/libusb-1.0.dll.a +# Rebuild the import lib from the .def so that MS and MinGW DLLs can be interchanged +@CREATE_IMPORT_LIB_TRUE@@OS_WINDOWS_TRUE@.libs/libusb-1.0.dll.a: libusb-1.0.def libusb-1.0.la +@CREATE_IMPORT_LIB_TRUE@@OS_WINDOWS_TRUE@ $(AM_V_DLLTOOL)$(DLLTOOL) $(DLLTOOLFLAGS) --kill-at --input-def $< --dllname libusb-1.0.dll --output-lib $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/core.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/core.c new file mode 100644 index 00000000..507066af --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/core.c @@ -0,0 +1,3086 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Core functions for libusb + * Copyright © 2012-2023 Nathan Hjelm + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" +#include "version.h" + +#ifdef __ANDROID__ +#include +#endif +#include +#include +#ifdef HAVE_SYSLOG +#include +#endif + +static const struct libusb_version libusb_version_internal = + { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, + LIBUSB_RC, "https://libusb.info" }; +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; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) +static usbi_atomic_t default_debug_level = -1; +#endif +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; + +/** + * \mainpage libusb-1.0 API Reference + * + * \section intro Introduction + * + * libusb is an open source library that allows you to communicate with USB + * devices from user space. For more info, see the + * libusb homepage. + * + * This documentation is aimed at application developers wishing to + * communicate with USB peripherals from their own software. After reviewing + * this documentation, feedback and questions can be sent to the + * libusb-devel mailing list. + * + * This documentation assumes knowledge of how to operate USB devices from + * a software standpoint (descriptors, configurations, interfaces, endpoints, + * control/bulk/interrupt/isochronous transfers, etc). Full information + * can be found in the USB 3.0 + * Specification which is available for free download. You can probably + * find less verbose introductions by searching the web. + * + * \section API Application Programming Interface (API) + * + * See the \ref libusb_api page for a complete list of the libusb functions. + * + * \section features Library features + * + * - All transfer types supported (control/bulk/interrupt/isochronous) + * - 2 transfer interfaces: + * -# Synchronous (simple) + * -# Asynchronous (more complicated, but more powerful) + * - Thread safe (although the asynchronous interface means that you + * usually won't need to thread) + * - Lightweight with lean API + * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer + * - Hotplug support (on some platforms). See \ref libusb_hotplug. + * + * \section gettingstarted Getting Started + * + * To begin reading the API documentation, start with the Topics page which + * links to the different categories of libusb's functionality. + * + * One decision you will have to make is whether to use the synchronous + * or the asynchronous data transfer interface. The \ref libusb_io documentation + * provides some insight into this topic. + * + * Some example programs can be found in the libusb source distribution under + * the "examples" subdirectory. The libusb homepage includes a list of + * real-life project examples which use libusb. + * + * \section errorhandling Error handling + * + * libusb functions typically return 0 on success or a negative error code + * on failure. These negative error codes relate to LIBUSB_ERROR constants + * which are listed on the \ref libusb_misc "miscellaneous" documentation page. + * + * \section msglog Debug message logging + * + * libusb uses stderr for all logging. By default, logging is set to NONE, + * which means that no output will be produced. However, unless the library + * has been compiled with logging disabled, then any application calls to + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), + * libusb_init_context, or the setting of the environmental variable + * LIBUSB_DEBUG outside of the application, can result in logging being + * produced. Your application should therefore not close stderr, but instead + * direct it to the null device if its output is undesirable. + * + * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) or + * libusb_init_context functions can be used to enable logging of certain + * messages. With the default configuration, libusb will not log much so if + * you are advised to use one of these functions to enable all + * error/warning/informational messages. It will help debug problems with your + * software. + * + * The logged messages are unstructured. There is no one-to-one correspondence + * between messages being logged and success or failure return codes from + * libusb functions. There is no format to the messages, so you should not + * try to capture or parse them. They are not and will not be localized. + * These messages are not intended to being passed to your application user; + * instead, you should interpret the error codes returned from libusb functions + * and provide appropriate notification to the user. The messages are simply + * there to aid you as a programmer, and if you're confused because you're + * getting a strange error code from a libusb function, enabling message + * logging may give you a suitable explanation. + * + * The LIBUSB_DEBUG environment variable can be used to enable message logging + * at run-time. This environment variable should be set to a log level number, + * which is interpreted the same as the + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or + * libusb_init_context(&ctx, &(struct libusb_init_option){.option = LIBUSB_OPTION_LOG_LEVEL, .value = {.ival = level}}, 0). + * When the environment variable is set, the message logging verbosity level is + * fixed and setting the LIBUSB_OPTION_LOG_LEVEL option has no effect. + * + * libusb can be compiled without any logging functions, useful for embedded + * systems. In this case, neither the LIBUSB_OPTION_LOG_LEVEL option, nor the + * LIBUSB_DEBUG environment variable will have any effect. + * + * libusb can also be compiled with verbose debugging messages always. When + * the library is compiled in this way, all messages of all verbosities are + * always logged. Again, in this case, neither the LIBUSB_OPTION_LOG_LEVEL + * option, nor the LIBUSB_DEBUG environment variable will have any effect. + * + * \section remarks Other remarks + * + * libusb does have imperfections. The \ref libusb_caveats "caveats" page attempts + * to document these. + */ + +/** + * \page libusb_caveats Caveats + * + * \section threadsafety Thread safety + * + * libusb is designed to be completely thread-safe, but as with any API it + * cannot prevent a user from sabotaging themselves, either intentionally or + * otherwise. + * + * Observe the following general guidelines: + * + * - Calls to functions that release a resource (e.g. libusb_close(), + * libusb_free_config_descriptor()) should not be called concurrently on + * the same resource. This is no different than concurrently calling free() + * on the same allocated pointer. + * - Each individual \ref libusb_transfer should be prepared by a single + * thread. In other words, no two threads should ever be concurrently + * filling out the fields of a \ref libusb_transfer. You can liken this to + * calling sprintf() with the same destination buffer from multiple threads. + * The results will likely not be what you want unless the input parameters + * are all the same, but its best to avoid this situation entirely. + * - Both the \ref libusb_transfer structure and its associated data buffer + * should not be accessed between the time the transfer is submitted and the + * time the completion callback is invoked. You can think of "ownership" of + * these things as being transferred to libusb while the transfer is active. + * - The various "setter" functions (e.g. libusb_set_log_cb(), + * libusb_set_pollfd_notifiers()) should not be called concurrently on the + * resource. Though doing so will not lead to any undefined behavior, it + * will likely produce results that the application does not expect. + * + * Rules for multiple threads and asynchronous I/O are detailed + * \ref libusb_mtasync "here". + * + * \section fork Fork considerations + * + * libusb is not designed to work across fork() calls. Depending on + * the platform, there may be resources in the parent process that are not + * available to the child (e.g. the hotplug monitor thread on Linux). In + * addition, since the parent and child will share libusb's internal file + * descriptors, using libusb in any way from the child could cause the parent + * process's \ref libusb_context to get into an inconsistent state. + * + * On Linux, libusb's file descriptors will be marked as CLOEXEC, which means + * that it is safe to fork() and exec() without worrying about the child + * process needing to clean up state or having access to these file descriptors. + * Other platforms may not be so forgiving, so consider yourself warned! + * + * \section devresets Device resets + * + * The libusb_reset_device() function allows you to reset a device. If your + * program has to call such a function, it should obviously be aware that + * the reset will cause device state to change (e.g. register values may be + * reset). + * + * The problem is that any other program could reset the device your program + * is working with, at any time. libusb does not offer a mechanism to inform + * you when this has happened, so if someone else resets your device it will + * not be clear to your own program why the device state has changed. + * + * Ultimately, this is a limitation of writing drivers in user space. + * Separation from the USB stack in the underlying kernel makes it difficult + * for the operating system to deliver such notifications to your program. + * The Linux kernel USB stack allows such reset notifications to be delivered + * to in-kernel USB drivers, but it is not clear how such notifications could + * be delivered to second-class drivers that live in user space. + * + * \section blockonly Blocking-only functionality + * + * The functionality listed below is only available through synchronous, + * blocking functions. There are no asynchronous/non-blocking alternatives, + * and no clear ways of implementing these. + * + * - Configuration activation (libusb_set_configuration()) + * - Interface/alternate setting activation (libusb_set_interface_alt_setting()) + * - Releasing of interfaces (libusb_release_interface()) + * - Clearing of halt/stall condition (libusb_clear_halt()) + * - Device resets (libusb_reset_device()) + * + * \section configsel Configuration selection and handling + * + * When libusb presents a device handle to an application, there is a chance + * that the corresponding device may be in unconfigured state. For devices + * with multiple configurations, there is also a chance that the configuration + * currently selected is not the one that the application wants to use. + * + * The obvious solution is to add a call to libusb_set_configuration() early + * on during your device initialization routines, but there are caveats to + * be aware of: + * -# If the device is already in the desired configuration, calling + * libusb_set_configuration() using the same configuration value will cause + * a lightweight device reset. This may not be desirable behaviour. + * -# In the case where the desired configuration is already active, libusb + * may not even be able to perform a lightweight device reset. For example, + * take my USB keyboard with fingerprint reader: I'm interested in driving + * the fingerprint reader interface through libusb, but the kernel's + * USB-HID driver will almost always have claimed the keyboard interface. + * Because the kernel has claimed an interface, it is not even possible to + * perform the lightweight device reset, so libusb_set_configuration() will + * fail. (Luckily the device in question only has a single configuration.) + * -# libusb will be unable to set a configuration if other programs or + * drivers have claimed interfaces. In particular, this means that kernel + * drivers must be detached from all the interfaces before + * libusb_set_configuration() may succeed. + * + * One solution to some of the above problems is to consider the currently + * active configuration. If the configuration we want is already active, then + * we don't have to select any configuration: +\code +cfg = -1; +libusb_get_configuration(dev, &cfg); +if (cfg != desired) + libusb_set_configuration(dev, desired); +\endcode + * + * This is probably suitable for most scenarios, but is inherently racy: + * another application or driver may change the selected configuration + * after the libusb_get_configuration() call. + * + * Even in cases where libusb_set_configuration() succeeds, consider that other + * applications or drivers may change configuration after your application + * calls libusb_set_configuration(). + * + * One possible way to lock your device into a specific configuration is as + * follows: + * -# Set the desired configuration (or use the logic above to realise that + * it is already in the desired configuration) + * -# Claim the interface that you wish to use + * -# Check that the currently active configuration is the one that you want + * to use. + * + * The above method works because once an interface is claimed, no application + * or driver is able to select another configuration. + * + * \section earlycomp Early transfer completion + * + * NOTE: This section is currently Linux-centric. I am not sure if any of these + * considerations apply to Darwin or other platforms. + * + * When a transfer completes early (i.e. when less data is received/sent in + * any one packet than the transfer buffer allows for) then libusb is designed + * to terminate the transfer immediately, not transferring or receiving any + * more data unless other transfers have been queued by the user. + * + * On legacy platforms, libusb is unable to do this in all situations. After + * the incomplete packet occurs, "surplus" data may be transferred. For recent + * versions of libusb, this information is kept (the data length of the + * transfer is updated) and, for device-to-host transfers, any surplus data was + * added to the buffer. Still, this is not a nice solution because it loses the + * information about the end of the short packet, and the user probably wanted + * that surplus data to arrive in the next logical transfer. + * + * \section zlp Zero length packets + * + * - libusb is able to send a packet of zero length to an endpoint simply by + * submitting a transfer of zero length. + * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET + * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux, + * Darwin and Windows (WinUSB). + */ + +/** + * \page libusb_contexts Contexts + * + * It is possible that libusb may be used simultaneously from two independent + * libraries linked into the same executable. For example, if your application + * has a plugin-like system which allows the user to dynamically load a range + * of modules into your program, it is feasible that two independently + * developed modules may both use libusb. + * + * libusb is written to allow for these multiple user scenarios. The two + * "instances" of libusb will not interfere: an option set by one user will have + * no effect the same option for other users, other users can continue using + * libusb after one of them calls libusb_exit(), etc. + * + * This is made possible through libusb's context concept. When you + * call libusb_init_context(), you are (optionally) given a context. You can then pass + * this context pointer back into future libusb functions. + * + * In order to keep things simple for more simplistic applications, it is + * legal to pass NULL to all functions requiring a context pointer (as long as + * you're sure no other code will attempt to use libusb from the same process). + * When you pass NULL, the default context will be used. The default context + * is created the first time a process calls libusb_init_context() when no other + * context is alive. Contexts are destroyed during libusb_exit(). + * + * The default context is reference-counted and can be shared. That means that + * if libusb_init_context(NULL, x, y) is called twice within the same process, the two + * users end up sharing the same context. The deinitialization and freeing of + * the default context will only happen when the last user calls libusb_exit(). + * In other words, the default context is created and initialized when its + * reference count goes from 0 to 1, and is deinitialized and destroyed when + * its reference count goes from 1 to 0. + * + * You may be wondering why only a subset of libusb functions require a + * context pointer in their function definition. Internally, libusb stores + * context pointers in other objects (e.g. libusb_device instances) and hence + * can infer the context from those objects. + */ + + /** + * \page libusb_api Application Programming Interface + * + * This is the complete list of libusb functions, structures and + * enumerations in alphabetical order. + * + * \section Functions + * - libusb_alloc_streams() + * - libusb_alloc_transfer() + * - libusb_attach_kernel_driver() + * - libusb_bulk_transfer() + * - libusb_cancel_transfer() + * - libusb_claim_interface() + * - libusb_clear_halt() + * - libusb_close() + * - libusb_control_transfer() + * - libusb_control_transfer_get_data() + * - libusb_control_transfer_get_setup() + * - libusb_cpu_to_le16() + * - libusb_detach_kernel_driver() + * - libusb_dev_mem_alloc() + * - libusb_dev_mem_free() + * - libusb_endpoint_set_raw_io() + * - libusb_endpoint_supports_raw_io() + * - libusb_error_name() + * - libusb_event_handler_active() + * - libusb_event_handling_ok() + * - libusb_exit() + * - libusb_fill_bulk_stream_transfer() + * - libusb_fill_bulk_transfer() + * - libusb_fill_control_setup() + * - libusb_fill_control_transfer() + * - libusb_fill_interrupt_transfer() + * - libusb_fill_iso_transfer() + * - libusb_free_bos_descriptor() + * - libusb_free_config_descriptor() + * - libusb_free_container_id_descriptor() + * - libusb_free_device_list() + * - libusb_free_pollfds() + * - libusb_free_ss_endpoint_companion_descriptor() + * - libusb_free_ss_usb_device_capability_descriptor() + * - libusb_free_streams() + * - libusb_free_transfer() + * - libusb_free_usb_2_0_extension_descriptor() + * - libusb_get_active_config_descriptor() + * - libusb_get_bos_descriptor() + * - libusb_get_session_data() + * - libusb_get_bus_number() + * - libusb_get_config_descriptor() + * - libusb_get_config_descriptor_by_value() + * - libusb_get_configuration() + * - libusb_get_container_id_descriptor() + * - libusb_get_descriptor() + * - libusb_get_device() + * - libusb_get_device_address() + * - libusb_get_device_descriptor() + * - libusb_get_device_list() + * - libusb_get_device_speed() + * - libusb_get_device_string() + * - libusb_get_iso_packet_buffer() + * - libusb_get_iso_packet_buffer_simple() + * - libusb_get_max_alt_packet_size() + * - libusb_get_max_iso_packet_size() + * - libusb_get_max_raw_io_transfer_size() + * - libusb_get_max_packet_size() + * - libusb_get_next_timeout() + * - libusb_get_parent() + * - libusb_get_pollfds() + * - libusb_get_port_number() + * - libusb_get_port_numbers() + * - libusb_get_port_path() + * - libusb_get_ss_endpoint_companion_descriptor() + * - libusb_get_ss_usb_device_capability_descriptor() + * - libusb_get_string_descriptor() + * - libusb_get_string_descriptor_ascii() + * - libusb_get_usb_2_0_extension_descriptor() + * - libusb_get_version() + * - libusb_handle_events() + * - libusb_handle_events_completed() + * - libusb_handle_events_locked() + * - libusb_handle_events_timeout() + * - libusb_handle_events_timeout_completed() + * - libusb_has_capability() + * - libusb_hotplug_deregister_callback() + * - libusb_hotplug_register_callback() + * - libusb_init() + * - libusb_init_context() + * - libusb_interrupt_event_handler() + * - libusb_interrupt_transfer() + * - libusb_kernel_driver_active() + * - libusb_lock_events() + * - libusb_lock_event_waiters() + * - libusb_open() + * - libusb_open_device_with_vid_pid() + * - libusb_pollfds_handle_timeouts() + * - libusb_ref_device() + * - libusb_release_interface() + * - libusb_reset_device() + * - libusb_set_auto_detach_kernel_driver() + * - libusb_set_configuration() + * - libusb_set_debug() + * - libusb_set_log_cb() + * - libusb_set_interface_alt_setting() + * - libusb_set_iso_packet_lengths() + * - libusb_set_option() + * - libusb_setlocale() + * - libusb_set_pollfd_notifiers() + * - libusb_strerror() + * - libusb_submit_transfer() + * - libusb_transfer_get_stream_id() + * - libusb_transfer_set_stream_id() + * - libusb_try_lock_events() + * - libusb_unlock_events() + * - libusb_unlock_event_waiters() + * - libusb_unref_device() + * - libusb_wait_for_event() + * - libusb_wrap_sys_device() + * + * \section Structures + * - libusb_bos_descriptor + * - libusb_bos_dev_capability_descriptor + * - libusb_config_descriptor + * - libusb_container_id_descriptor + * - \ref libusb_context + * - libusb_control_setup + * - \ref libusb_device + * - libusb_device_descriptor + * - \ref libusb_device_handle + * - libusb_endpoint_descriptor + * - libusb_interface + * - libusb_interface_descriptor + * - libusb_iso_packet_descriptor + * - libusb_pollfd + * - libusb_ss_endpoint_companion_descriptor + * - libusb_ss_usb_device_capability_descriptor + * - libusb_transfer + * - libusb_usb_2_0_extension_descriptor + * - libusb_version + * + * \section Enums + * - \ref libusb_bos_type + * - \ref libusb_capability + * - \ref libusb_class_code + * - \ref libusb_descriptor_type + * - \ref libusb_device_string_type + * - \ref libusb_endpoint_direction + * - \ref libusb_endpoint_transfer_type + * - \ref libusb_error + * - \ref libusb_iso_sync_type + * - \ref libusb_iso_usage_type + * - \ref libusb_log_level + * - \ref libusb_option + * - \ref libusb_request_recipient + * - \ref libusb_request_type + * - \ref libusb_speed + * - \ref libusb_ss_usb_device_capability_attributes + * - \ref libusb_standard_request + * - \ref libusb_supported_speed + * - \ref libusb_transfer_flags + * - \ref libusb_transfer_status + * - \ref libusb_transfer_type + * - \ref libusb_usb_2_0_extension_attributes + */ + +/** + * @defgroup libusb_lib Library initialization/deinitialization + * This page details how to initialize and deinitialize libusb. Initialization + * must be performed before using any libusb functionality, and similarly you + * must not call any libusb functions after deinitialization. + */ + +/** + * @defgroup libusb_dev Device handling and enumeration + * The functionality documented below is designed to help with the following + * operations: + * - Enumerating the USB devices currently attached to the system + * - Choosing a device to operate from your software + * - Opening and closing the chosen device + * + * \section nutshell In a nutshell... + * + * The description below really makes things sound more complicated than they + * actually are. The following sequence of function calls will be suitable + * for almost all scenarios and does not require you to have such a deep + * understanding of the resource management issues: + * \code +// discover devices +libusb_device **list; +libusb_device *found = NULL; +ssize_t cnt = libusb_get_device_list(NULL, &list); +ssize_t i = 0; +int err = 0; +if (cnt < 0) + error(); + +for (i = 0; i < cnt; i++) { + libusb_device *device = list[i]; + if (is_interesting(device)) { + found = device; + break; + } +} + +if (found) { + libusb_device_handle *handle; + + err = libusb_open(found, &handle); + if (err) + error(); + // etc +} + +libusb_free_device_list(list, 1); +\endcode + * + * The two important points: + * - You asked libusb_free_device_list() to unreference the devices (2nd + * parameter) + * - You opened the device before freeing the list and unreferencing the + * devices + * + * If you ended up with a handle, you can now proceed to perform I/O on the + * device. + * + * \section devshandles Devices and device handles + * libusb has a concept of a USB device, represented by the + * \ref libusb_device opaque type. A device represents a USB device that + * is currently or was previously connected to the system. Using a reference + * to a device, you can determine certain information about the device (e.g. + * you can read the descriptor data). + * + * 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. 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 + * permission to operate such device, or another program or driver may be + * using the device. + * + * When you've found a device that you'd like to operate, you must ask + * libusb to open the device using the libusb_open() function. Assuming + * success, libusb then returns you a device handle + * (a \ref libusb_device_handle pointer). All "real" I/O operations then + * operate on the handle rather than the original device pointer. + * + * \section devref Device discovery and reference counting + * + * Device discovery (i.e. calling libusb_get_device_list()) returns a + * freshly-allocated list of devices. The list itself must be freed when + * you are done with it. libusb also needs to know when it is OK to free + * the contents of the list - the devices themselves. + * + * To handle these issues, libusb provides you with two separate items: + * - A function to free the list itself + * - A reference counting system for the devices inside + * + * New devices presented by the libusb_get_device_list() function all have a + * reference count of 1. You can increase and decrease reference count using + * libusb_ref_device() and libusb_unref_device(). A device is destroyed when + * its reference count reaches 0. + * + * With the above information in mind, the process of opening a device can + * be viewed as follows: + * -# 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. + * + * The order is important - you must not unreference the device before + * attempting to open it, because unreferencing it may destroy the device. + * + * For convenience, the libusb_free_device_list() function includes a + * parameter to optionally unreference all the devices in the list before + * freeing the list itself. This combines steps 3 and 4 above. + * + * As an implementation detail, libusb_open() actually adds a reference to + * the device in question. This is because the device remains available + * through the handle via libusb_get_device(). The reference is deleted during + * libusb_close(). + */ + +/** @defgroup libusb_misc Miscellaneous */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * 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 16 + +static struct discovered_devs *discovered_devs_alloc(void) +{ + struct discovered_devs *ret = + malloc(sizeof(*ret) + (sizeof(void *) * DISCOVERED_DEVICES_SIZE_STEP)); + + if (ret) { + ret->len = 0; + ret->capacity = DISCOVERED_DEVICES_SIZE_STEP; + } + return ret; +} + +static void discovered_devs_free(struct discovered_devs *discdevs) +{ + size_t i; + + for (i = 0; i < discdevs->len; i++) + libusb_unref_device(discdevs->devices[i]); + + free(discdevs); +} + +/* append a device to the discovered devices collection. may realloc itself, + * returning new discdevs. returns NULL on realloc failure. */ +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev) +{ + size_t len = discdevs->len; + size_t capacity; + struct discovered_devs *new_discdevs; + + /* if there is space, just append the device */ + if (len < discdevs->capacity) { + discdevs->devices[len] = libusb_ref_device(dev); + discdevs->len++; + return discdevs; + } + + /* exceeded capacity, need to grow */ + 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. */ + new_discdevs = realloc(discdevs, + sizeof(*discdevs) + (sizeof(void *) * capacity)); + if (!new_discdevs) { + discovered_devs_free(discdevs); + return NULL; + } + + discdevs = new_discdevs; + discdevs->capacity = capacity; + discdevs->devices[len] = libusb_ref_device(dev); + discdevs->len++; + + return discdevs; +} + +/* Allocate a new device with a specific session ID. The returned device has + * a reference count of 1. */ +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id) +{ + size_t priv_size = usbi_backend.device_priv_size; + struct libusb_device *dev = calloc(1, PTR_ALIGN(sizeof(*dev)) + priv_size); + + if (!dev) + return NULL; + + usbi_atomic_store(&dev->refcnt, 1); + + dev->ctx = ctx; + dev->session_data = session_id; + dev->speed = LIBUSB_SPEED_UNKNOWN; + + /* Note: the device is NOT added to ctx->usb_devs here. The caller + * (backend) must call usbi_connect_device() after fully initializing + * the device's private data. This prevents a concurrent + * usbi_get_device_by_session_id() from finding a half-initialized + * device. */ + + return dev; +} + +void usbi_attach_device(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + + usbi_atomic_store(&dev->attached, 1); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_add(&dev->list, &ctx->usb_devs); + usbi_mutex_unlock(&ctx->usb_devs_lock); +} + +void usbi_connect_device(struct libusb_device *dev) +{ + usbi_attach_device(dev); + usbi_hotplug_notification(DEVICE_CTX(dev), dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); +} + +void usbi_detach_device(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + + usbi_atomic_store(&dev->attached, 0); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_del(&dev->list); + usbi_mutex_unlock(&ctx->usb_devs_lock); +} + +void usbi_disconnect_device(struct libusb_device *dev) +{ + usbi_detach_device(dev); + usbi_hotplug_notification(DEVICE_CTX(dev), dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); +} + +/* Perform some final sanity checks on a newly discovered device. If this + * function fails (negative return code), the device should not be added + * to the discovered device list. */ +int usbi_sanitize_device(struct libusb_device *dev) +{ + uint8_t num_configurations; + + if (dev->device_descriptor.bLength != LIBUSB_DT_DEVICE_SIZE || + dev->device_descriptor.bDescriptorType != LIBUSB_DT_DEVICE) { + usbi_err(DEVICE_CTX(dev), "invalid device descriptor"); + return LIBUSB_ERROR_IO; + } + + num_configurations = dev->device_descriptor.bNumConfigurations; + if (num_configurations > USB_MAXCONFIG) { + usbi_err(DEVICE_CTX(dev), "too many configurations"); + return LIBUSB_ERROR_IO; + } else if (0 == num_configurations) { + usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device"); + } + + return 0; +} + +/* Examine libusb's internal list of known devices, looking for one with + * a specific session ID. Returns the matching device if it was found, and + * NULL otherwise. */ +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id) +{ + struct libusb_device *dev; + struct libusb_device *ret = NULL; + + usbi_mutex_lock(&ctx->usb_devs_lock); + for_each_device(ctx, dev) { + if (dev->session_data == session_id) { + ret = libusb_ref_device(dev); + break; + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + return ret; +} + +/** @ingroup libusb_dev + * Returns a list of USB devices currently attached to the system. This is + * your entry point into finding a USB device to operate. + * + * You are expected to unreference all the devices when you are done with + * them, and then free the list with libusb_free_device_list(). Note that + * libusb_free_device_list() can unref all the devices for you. Be careful + * not to unreference a device you are about to open until after you have + * opened it. + * + * This return value of this function indicates the number of devices in + * the resultant list. The list is actually one element larger, as it is + * NULL-terminated. + * + * \param ctx the context to operate on, or NULL for the default context + * \param list output location for a list of devices. Must be later freed with + * libusb_free_device_list(). + * \returns the number of devices in the outputted list, or any + * \ref libusb_error according to errors encountered by the backend. + */ +ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, + libusb_device ***list) +{ + struct discovered_devs *discdevs = discovered_devs_alloc(); + struct libusb_device **ret; + int r = 0; + ssize_t i, len; + + usbi_dbg(ctx, " "); + + if (!discdevs) + return LIBUSB_ERROR_NO_MEM; + + ctx = usbi_get_context(ctx); + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* backend provides hotplug support */ + struct libusb_device *dev; + + if (usbi_backend.hotplug_poll) + usbi_backend.hotplug_poll(); + + usbi_mutex_lock(&ctx->usb_devs_lock); + for_each_device(ctx, dev) { + discdevs = discovered_devs_append(discdevs, dev); + + if (!discdevs) { + r = LIBUSB_ERROR_NO_MEM; + break; + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } else { + /* backend does not provide hotplug support */ + r = usbi_backend.get_device_list(ctx, &discdevs); + } + + if (r < 0) { + len = r; + goto out; + } + + /* convert discovered_devs into a list */ + len = (ssize_t)discdevs->len; + ret = calloc((size_t)len + 1, sizeof(struct libusb_device *)); + if (!ret) { + len = LIBUSB_ERROR_NO_MEM; + goto out; + } + + ret[len] = NULL; + for (i = 0; i < len; i++) { + struct libusb_device *dev = discdevs->devices[i]; + ret[i] = libusb_ref_device(dev); + } + *list = ret; + +out: + if (discdevs) + discovered_devs_free(discdevs); + return len; +} + +/** \ingroup libusb_dev + * Frees a list of devices previously discovered using + * libusb_get_device_list(). If the unref_devices parameter is set, the + * reference count of each device in the list is decremented by 1. + * \param list the list to free + * \param unref_devices whether to unref the devices in the list + */ +void API_EXPORTED libusb_free_device_list(libusb_device **list, + int unref_devices) +{ + if (!list) + return; + + if (unref_devices) { + int i = 0; + struct libusb_device *dev; + + while ((dev = list[i++]) != NULL) + libusb_unref_device(dev); + } + free(list); +} + +/** \ingroup libusb_dev + * Returns the backend-specific identifier of the underlying system device tree + * node. Can be used to find the corresponding system device and directly query + * it (or access it otherwise) when and if necessary. + * + * Relevant backends: + * - Darwin: IOKit `sessionID` + * - Windows WinUSB: `DEVINST` + * - Linux, BSD: `busnum << 8 | devnum` + * + * Since version 1.0.30, \ref LIBUSB_API_VERSION >= 0x0100010C + * + * \param dev a device (must not be null) + * \returns the backend-specific device identifier + */ +unsigned long API_EXPORTED libusb_get_session_data(libusb_device *dev) +{ + return dev->session_data; +} + +/** \ingroup libusb_dev + * Get the number of the bus that a device is connected to. + * \param dev a device + * \returns the bus number + */ +uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev) +{ + return dev->bus_number; +} + +/** \ingroup libusb_dev + * Get the number of the port that a device is connected to. + * Unless the OS does something funky, or you are hot-plugging USB extension cards, + * the port number returned by this call is usually guaranteed to be uniquely tied + * to a physical port, meaning that different devices plugged on the same physical + * port should return the same port number. + * + * But outside of this, there is no guarantee that the port number returned by this + * call will remain the same, or even match the order in which ports have been + * numbered by the HUB/HCD manufacturer. + * + * \param dev a device + * \returns the port number (0 if not available) + */ +uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) +{ + return dev->port_number; +} + +/** \ingroup libusb_dev + * Get the list of all port numbers from root for the specified device + * + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 + * \param dev a device + * \param port_numbers the array that should contain the port numbers + * \param port_numbers_len the maximum length of the array. As per the USB 3.0 + * specs, the current maximum limit for the depth is 7. + * \returns the number of elements filled + * \returns \ref LIBUSB_ERROR_OVERFLOW if the array is too small + */ +int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, + uint8_t *port_numbers, int port_numbers_len) +{ + int i = port_numbers_len; + struct libusb_context *ctx = DEVICE_CTX(dev); + + if (port_numbers_len <= 0) + return LIBUSB_ERROR_INVALID_PARAM; + + /* HCDs can be listed as devices with port #0 */ + while((dev) && (dev->port_number != 0)) { + if (--i < 0) { + usbi_warn(ctx, "port numbers array is too small"); + return LIBUSB_ERROR_OVERFLOW; + } + port_numbers[i] = dev->port_number; + dev = dev->parent_dev; + } + if (i < port_numbers_len) + memmove(port_numbers, &port_numbers[i], (size_t)(port_numbers_len - i)); + return port_numbers_len - i; +} + +/** \ingroup libusb_dev + * \deprecated Please use \ref libusb_get_port_numbers() instead. + */ +int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev, + uint8_t *port_numbers, uint8_t port_numbers_len) +{ + UNUSED(ctx); + + return libusb_get_port_numbers(dev, port_numbers, port_numbers_len); +} + +/** \ingroup libusb_dev + * Get the the parent from the specified device. + * \param dev a device + * \returns the device parent or NULL if not available + * You should issue a \ref libusb_get_device_list() before calling this + * function and make sure that you only access the parent before issuing + * \ref libusb_free_device_list(). The reason is that libusb currently does + * not maintain a permanent list of device instances, and therefore can + * only guarantee that parents are fully instantiated within a + * libusb_get_device_list() - libusb_free_device_list() block. + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev) +{ + return dev->parent_dev; +} + +/** \ingroup libusb_dev + * Get the address of the device on the bus it is connected to. + * \param dev a device + * \returns the device address + */ +uint8_t API_EXPORTED libusb_get_device_address(libusb_device *dev) +{ + return dev->device_address; +} + +/** \ingroup libusb_dev + * Get the negotiated connection speed for a device. + * \param dev a device + * \returns a \ref libusb_speed code, where LIBUSB_SPEED_UNKNOWN means that + * the OS doesn't know or doesn't support returning the negotiated speed. + */ +int API_EXPORTED libusb_get_device_speed(libusb_device *dev) +{ + return (int)(dev->speed); +} + +static const struct libusb_endpoint_descriptor *find_endpoint( + struct libusb_config_descriptor *config, unsigned char endpoint) +{ + int iface_idx; + for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { + const struct libusb_interface *iface = &config->interface[iface_idx]; + int altsetting_idx; + + for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; + altsetting_idx++) { + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep; + } + } + } + return NULL; +} + +/** \ingroup libusb_dev + * Convenience function to retrieve the wMaxPacketSize value for a particular + * endpoint in the active device configuration. + * + * This function was originally intended to be of assistance when setting up + * isochronous transfers, but a design mistake resulted in this function + * instead. It simply returns the wMaxPacketSize value without considering + * its contents. If you're dealing with isochronous transfers, you probably + * want libusb_get_max_iso_packet_size() instead. + * + * \param dev a device + * \param endpoint address of the endpoint in question + * \returns the wMaxPacketSize value + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + */ +int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_endpoint(config, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = ep->wMaxPacketSize; + +out: + libusb_free_config_descriptor(config); + return r; +} + +static const struct libusb_endpoint_descriptor *find_alt_endpoint( + struct libusb_config_descriptor *config, + int iface_idx, int altsetting_idx, unsigned char endpoint) +{ + if (iface_idx >= config->bNumInterfaces) { + return NULL; + } + + const struct libusb_interface *iface = &config->interface[iface_idx]; + + if (altsetting_idx >= iface->num_altsetting) { + return NULL; + } + + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep; + } + return NULL; +} + +static int get_endpoint_max_packet_size(libusb_device *dev, + const struct libusb_endpoint_descriptor *ep) +{ + struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; + enum libusb_endpoint_transfer_type ep_type; + uint16_t val; + int r = 0; + int speed; + + speed = libusb_get_device_speed(dev); + if (speed >= LIBUSB_SPEED_SUPER) { + r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); + if (r == LIBUSB_SUCCESS) { + r = ss_ep_cmp->wBytesPerInterval; + libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); + } + } + + /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ + if (speed < LIBUSB_SPEED_SUPER || r < 0) { + val = ep->wMaxPacketSize; + ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + } + + return r; +} + +/** \ingroup libusb_dev + * Calculate the maximum packet size which a specific endpoint is capable is + * sending or receiving in the duration of 1 microframe + * + * Only the active configuration is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * This function only considers the first alternate setting of the interface. + * If the endpoint has different maximum packet sizes for different alternate + * settings, you probably want libusb_get_max_alt_packet_size() instead. + * + * Since v1.0.3. + * + * \param dev a device + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_alt_packet_size + */ +int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_endpoint(config, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = get_endpoint_max_packet_size(dev, ep); + +out: + libusb_free_config_descriptor(config); + return r; +} + +/** \ingroup libusb_dev + * Calculate the maximum packet size which a specific endpoint is capable of + * sending or receiving in the duration of 1 microframe + * + * Only the active configuration is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param dev a device + * \param interface_number the bInterfaceNumber of the interface + * the endpoint belongs to + * \param alternate_setting the bAlternateSetting of the interface + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_iso_packet_size + */ +int API_EXPORTED libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_alt_endpoint(config, interface_number, + alternate_setting, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = get_endpoint_max_packet_size(dev, ep); + +out: + libusb_free_config_descriptor(config); + return r; +} + +/** \ingroup libusb_dev + * Increment the reference count of a device. + * \param dev the device to reference + * \returns the same device + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) +{ + long refcnt; + + refcnt = usbi_atomic_inc(&dev->refcnt); + assert(refcnt >= 2); + UNUSED(refcnt); + + return dev; +} + +/** \ingroup libusb_dev + * Decrement the reference count of a device. If the decrement operation + * causes the reference count to reach zero, the device shall be destroyed. + * \param dev the device to unreference + */ +void API_EXPORTED libusb_unref_device(libusb_device *dev) +{ + long refcnt; + + if (!dev) + return; + + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* Non-hotplug path: the decrement and conditional list removal + * must be atomic with respect to usbi_get_device_by_session_id(), + * which searches the list and refs the device under the same lock. + * Without this, a concurrent search could find a device whose + * refcnt has already reached zero and hit the assert in + * libusb_ref_device(). */ + struct libusb_context *ctx = DEVICE_CTX(dev); + + usbi_mutex_lock(&ctx->usb_devs_lock); + refcnt = usbi_atomic_dec(&dev->refcnt); + assert(refcnt >= 0); + if (refcnt == 0 && usbi_atomic_load(&dev->attached)) { + list_del(&dev->list); + usbi_atomic_store(&dev->attached, 0); + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } else { + refcnt = usbi_atomic_dec(&dev->refcnt); + assert(refcnt >= 0); + } + + if (refcnt == 0) { + usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address); + + libusb_unref_device(dev->parent_dev); + + if (usbi_backend.destroy_device) + usbi_backend.destroy_device(dev); + + for (int idx = 0; idx < LIBUSB_DEVICE_STRING_COUNT; ++idx) { + free(dev->device_strings_utf8[idx]); + } + + free(dev); + } +} + +/** \ingroup libusb_dev + * Wrap a platform-specific system device handle and obtain a libusb device + * handle for the underlying device. The handle allows you to use libusb to + * perform I/O on the device in question. + * + * Call libusb_init_context with the LIBUSB_OPTION_NO_DEVICE_DISCOVERY + * option 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. Setting this option with libusb_set_option is deprecated. + * + * On Linux, the system device handle must be a valid file descriptor opened + * on the device node. + * + * The system device handle must remain open until libusb_close() is called. + * The system device handle will not be closed by libusb_close(). + * + * Internally, this function creates a temporary device and makes it + * available to you through libusb_get_device(). This device is destroyed + * during libusb_close(). The device shall not be opened through libusb_open(). + * + * 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 + * populated when the return code is 0. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this + * platform + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, + libusb_device_handle **dev_handle) +{ + struct libusb_device_handle *_dev_handle; + size_t priv_size = usbi_backend.device_handle_priv_size; + int r; + + usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); + + ctx = usbi_get_context(ctx); + + if (!usbi_backend.wrap_sys_device) + return LIBUSB_ERROR_NOT_SUPPORTED; + + _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size); + if (!_dev_handle) + return LIBUSB_ERROR_NO_MEM; + + usbi_mutex_init(&_dev_handle->lock); + + r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev); + if (r < 0) { + 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; + } + + usbi_mutex_lock(&ctx->open_devs_lock); + list_add(&_dev_handle->list, &ctx->open_devs); + usbi_mutex_unlock(&ctx->open_devs_lock); + *dev_handle = _dev_handle; + + return 0; +} + +/** \ingroup libusb_dev + * Open a device and obtain a device handle. A handle allows you to perform + * I/O on the device in question. + * + * Internally, this function adds a reference to the device and makes it + * available to you through libusb_get_device(). This reference is removed + * during libusb_close(). + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param dev the device to open + * \param dev_handle output location for the returned device handle pointer. Only + * populated when the return code is 0. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_open(libusb_device *dev, + libusb_device_handle **dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device_handle *_dev_handle; + size_t priv_size = usbi_backend.device_handle_priv_size; + int r; + + 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) + return LIBUSB_ERROR_NO_MEM; + + usbi_mutex_init(&_dev_handle->lock); + + _dev_handle->dev = libusb_ref_device(dev); + + r = usbi_backend.open(_dev_handle); + if (r < 0) { + 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); + return r; + } + + usbi_mutex_lock(&ctx->open_devs_lock); + list_add(&_dev_handle->list, &ctx->open_devs); + usbi_mutex_unlock(&ctx->open_devs_lock); + *dev_handle = _dev_handle; + + return 0; +} + +/** \ingroup libusb_dev + * Convenience function for finding a device with a particular + * idVendor/idProduct combination. This function is intended + * for those scenarios where you are using libusb to knock up a quick test + * application - it allows you to avoid calling libusb_get_device_list() and + * worrying about traversing/freeing the list. + * + * This function has limitations and is hence not intended for use in real + * applications: if multiple devices have the same IDs it will only + * give you the first one, etc. + * + * \param ctx the context to operate on, or NULL for the default context + * \param vendor_id the idVendor value to search for + * \param product_id the idProduct value to search for + * \returns a device handle for the first found device, or NULL on error + * or if the device could not be found. */ +DEFAULT_VISIBILITY +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id) +{ + struct libusb_device **devs; + struct libusb_device *found = NULL; + struct libusb_device *dev; + struct libusb_device_handle *dev_handle = NULL; + size_t i = 0; + int r; + + if (libusb_get_device_list(ctx, &devs) < 0) + return NULL; + + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) + goto out; + if (desc.idVendor == vendor_id && desc.idProduct == product_id) { + found = dev; + break; + } + } + + if (found) { + r = libusb_open(found, &dev_handle); + if (r < 0) + dev_handle = NULL; + } + +out: + libusb_free_device_list(devs, 1); + return dev_handle; +} + +static void do_close(struct libusb_context *ctx, + struct libusb_device_handle *dev_handle) +{ + struct usbi_transfer *itransfer; + struct usbi_transfer *tmp; + + /* remove any transfers in flight that are for this device */ + usbi_mutex_lock(&ctx->flying_transfers_lock); + + /* safe iteration because transfers may be being deleted */ + for_each_transfer_safe(ctx, itransfer, tmp) { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + uint32_t state_flags; + + if (transfer->dev_handle != dev_handle) + continue; + + usbi_mutex_lock(&itransfer->lock); + state_flags = itransfer->state_flags; + usbi_mutex_unlock(&itransfer->lock); + if (!(state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); + + if (state_flags & USBI_TRANSFER_CANCELLING) + usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); + else + usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); + } + + /* remove from the list of in-flight transfers and make sure + * we don't accidentally use the device handle in the future + * (or that such accesses will be easily caught and identified as a crash) + */ + list_del(&itransfer->list); + transfer->dev_handle = NULL; + + /* it is up to the user to free up the actual transfer struct. this is + * just making sure that we don't attempt to process the transfer after + * the device handle is invalid + */ + usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed", + (void *) transfer, (void *) dev_handle); + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + usbi_mutex_lock(&ctx->open_devs_lock); + list_del(&dev_handle->list); + usbi_mutex_unlock(&ctx->open_devs_lock); + + usbi_backend.close(dev_handle); + libusb_unref_device(dev_handle->dev); + usbi_mutex_destroy(&dev_handle->lock); + free(dev_handle); +} + +/** \ingroup libusb_dev + * Close a device handle. Should be called on all open handles before your + * application exits. + * + * Internally, this function destroys the reference that was added by + * libusb_open() on the given device. + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param dev_handle the device handle to close + */ +void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx; + unsigned int event_flags; + int handling_events; + + if (!dev_handle) + return; + 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 + * at this point. More importantly, we want to perform the actual close of + * the device while holding the event handling lock (preventing any other + * thread from doing event handling) because we will be removing a file + * descriptor from the polling loop. If this is being called by the current + * event handler, we can bypass the interruption code because we already + * hold the event handling lock. */ + + if (!handling_events) { + /* Record that we are closing a device. + * Only signal an event if there are no prior pending events. */ + usbi_mutex_lock(&ctx->event_data_lock); + event_flags = ctx->event_flags; + if (!ctx->device_close++) + ctx->event_flags |= USBI_EVENT_DEVICE_CLOSE; + if (!event_flags) + usbi_signal_event(&ctx->event); + usbi_mutex_unlock(&ctx->event_data_lock); + + /* take event handling lock */ + libusb_lock_events(ctx); + } + + /* Close the device */ + do_close(ctx, dev_handle); + + if (!handling_events) { + /* We're done with closing this device. + * Clear the event pipe if there are no further pending events. */ + usbi_mutex_lock(&ctx->event_data_lock); + if (!--ctx->device_close) + ctx->event_flags &= ~USBI_EVENT_DEVICE_CLOSE; + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); + usbi_mutex_unlock(&ctx->event_data_lock); + + /* Release event handling lock and wake up event waiters */ + libusb_unlock_events(ctx); + } +} + +/** \ingroup libusb_dev + * Get the underlying device for a device handle. This function does not modify + * the reference count of the returned device, so do not feel compelled to + * unreference it when you are done. + * \param dev_handle a device handle + * \returns the underlying device + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle) +{ + return dev_handle->dev; +} + +/** \ingroup libusb_dev + * Determine the bConfigurationValue of the currently active configuration. + * + * You could formulate your own control request to obtain this information, + * but this function has the advantage that it may be able to retrieve the + * information from operating system caches (no I/O involved). + * + * If the OS does not cache this information, then this function will block + * while a control transfer is submitted to retrieve the information. + * + * This function will return a value of 0 in the config output + * parameter if the device is in unconfigured state. + * + * \param dev_handle a device handle + * \param config output location for the bConfigurationValue of the active + * configuration (only valid for return code 0) + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, + int *config) +{ + int r = LIBUSB_ERROR_NOT_SUPPORTED; + uint8_t tmp = 0; + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + + usbi_dbg(ctx, " "); + if (usbi_backend.get_configuration) + r = usbi_backend.get_configuration(dev_handle, &tmp); + + if (r == LIBUSB_ERROR_NOT_SUPPORTED) { + 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(ctx, "zero bytes returned in ctrl transfer?"); + r = LIBUSB_ERROR_IO; + } else { + usbi_dbg(ctx, "control failed, error %d", r); + } + } + + if (r == 0) { + usbi_dbg(ctx, "active config %u", tmp); + *config = (int)tmp; + } + + return r; +} + +/** \ingroup libusb_dev + * Set the active configuration for a device. + * + * The operating system may or may not have already set an active + * configuration on the device. It is up to your application to ensure the + * correct configuration is selected before you attempt to claim interfaces + * and perform other operations. + * + * If you call this function on a device already configured with the selected + * configuration, then this function will act as a lightweight device reset: + * it will issue a SET_CONFIGURATION request using the current configuration, + * causing most USB-related device state to be reset (altsetting reset to zero, + * endpoint halts cleared, toggles reset). + * + * Not all backends support setting the configuration from user space, which + * will be indicated by the return code \ref LIBUSB_ERROR_NOT_SUPPORTED. As this + * suggests that the platform is handling the device configuration itself, + * this error should generally be safe to ignore. + * + * You cannot change/reset configuration if your application has claimed + * interfaces. It is advised to set the desired configuration before claiming + * interfaces. + * + * Alternatively you can call libusb_release_interface() first. Note if you + * do things this way you must ensure that auto_detach_kernel_driver for + * dev is 0, otherwise the kernel driver will be re-attached when you + * release the interface(s). + * + * You cannot change/reset configuration if other applications or drivers have + * claimed interfaces. + * + * A configuration value of -1 will put the device in unconfigured state. + * The USB specifications state that a configuration value of 0 does this, + * however buggy devices exist which actually have a configuration 0. + * + * You should always use this function rather than formulating your own + * SET_CONFIGURATION control request. This is because the underlying operating + * system needs to know when such changes happen. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param configuration the bConfigurationValue of the configuration you + * wish to activate, or -1 if you wish to put the device in an unconfigured + * state + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist + * \returns \ref LIBUSB_ERROR_BUSY if interfaces are currently claimed + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration + * is not supported by the backend + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, + int 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); +} + +/** \ingroup libusb_dev + * Claim an interface on a given device handle. You must claim the interface + * you wish to use before you can perform I/O on any of its endpoints. + * + * It is legal to attempt to claim an already-claimed interface, in which + * case libusb just returns 0 without doing anything. + * + * If auto_detach_kernel_driver is set to 1 for dev, the kernel driver + * will be detached if necessary, on failure the detach error is returned. + * + * Claiming of interfaces is a purely logical operation; it does not cause + * any requests to be sent over the bus. Interface claiming is used to + * instruct the underlying operating system that your application wishes + * to take ownership of the interface. + * + * This is a non-blocking function. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the interface you + * wish to claim + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist + * \returns \ref LIBUSB_ERROR_BUSY if another program or driver has claimed the + * interface + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns a LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number) +{ + int r = 0; + + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + if (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_mutex_lock(&dev_handle->lock); + if (dev_handle->claimed_interfaces & (1U << interface_number)) + goto out; + + r = usbi_backend.claim_interface(dev_handle, (uint8_t)interface_number); + if (r == 0) + dev_handle->claimed_interfaces |= 1U << interface_number; + +out: + usbi_mutex_unlock(&dev_handle->lock); + return r; +} + +/** \ingroup libusb_dev + * Release an interface previously claimed with libusb_claim_interface(). You + * should release all claimed interfaces before closing a device handle. + * + * This is a blocking function. A SET_INTERFACE control request will be sent + * to the device, resetting interface state to the first alternate setting. + * + * If auto_detach_kernel_driver is set to 1 for dev, the kernel + * driver will be re-attached after releasing the interface. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the + * previously-claimed interface + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number) +{ + int r; + + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_mutex_lock(&dev_handle->lock); + if (!(dev_handle->claimed_interfaces & (1U << interface_number))) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = usbi_backend.release_interface(dev_handle, (uint8_t)interface_number); + if (r == 0) + dev_handle->claimed_interfaces &= ~(1U << interface_number); + +out: + usbi_mutex_unlock(&dev_handle->lock); + return r; +} + +/** \ingroup libusb_dev + * Activate an alternate setting for an interface. The interface must have + * been previously claimed with libusb_claim_interface(). + * + * You should always use this function rather than formulating your own + * SET_INTERFACE control request. This is because the underlying operating + * system needs to know when such changes happen. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the + * previously-claimed interface + * \param alternate_setting the bAlternateSetting of the alternate + * setting to activate + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the + * requested alternate setting does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting) +{ + 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; + + if (!usbi_atomic_load(&dev_handle->dev->attached)) { + 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; + } + usbi_mutex_unlock(&dev_handle->lock); + + return usbi_backend.set_interface_altsetting(dev_handle, + (uint8_t)interface_number, (uint8_t)alternate_setting); +} + +/** \ingroup libusb_dev + * Clear the halt/stall condition for an endpoint. Endpoints with halt status + * are unable to receive or transmit data until the halt condition is stalled. + * + * You should cancel all pending transfers before attempting to clear the halt + * condition. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param endpoint the endpoint to clear halt status + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint) +{ + 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); +} + +/** \ingroup libusb_dev + * Perform a USB port reset to reinitialize a device. The system will attempt + * to restore the previous configuration and alternate settings after the + * reset has completed. + * + * If the reset fails, the descriptors change, or the previous state cannot be + * restored, the device will appear to be disconnected and reconnected. This + * means that the device handle is no longer valid (you should close it) and + * rediscover the device. A return code of \ref LIBUSB_ERROR_NOT_FOUND indicates + * when this is the case. + * + * This is a blocking function which usually incurs a noticeable delay. + * + * \param dev_handle a handle of the device to reset + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the + * device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) +{ + usbi_dbg(HANDLE_CTX(dev_handle), " "); + if (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.reset_device) + return usbi_backend.reset_device(dev_handle); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_asyncio + * Allocate up to num_streams usb bulk streams on the specified endpoints. This + * function takes an array of endpoints rather then a single endpoint because + * some protocols require that endpoints are setup with similar stream ids. + * All endpoints passed in must belong to the same interface. + * + * Note this function may return less streams then requested. Also note that the + * same number of streams are allocated for each endpoint in the endpoint array. + * + * Stream id 0 is reserved, and should not be used to communicate with devices. + * If libusb_alloc_streams() returns with a value of N, you may use stream ids + * 1 to N. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param dev_handle a device handle + * \param num_streams number of streams to try to allocate + * \param endpoints array of endpoints to allocate streams on + * \param num_endpoints length of the endpoints array + * \returns number of streams allocated, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int 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 (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.alloc_streams) + return usbi_backend.alloc_streams(dev_handle, num_streams, endpoints, + num_endpoints); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_asyncio + * Free usb bulk streams allocated with libusb_alloc_streams(). + * + * Note streams are automatically free-ed when releasing an interface. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param dev_handle a device handle + * \param endpoints array of endpoints to free streams on + * \param num_endpoints length of the endpoints array + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints) +{ + usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints); + + if (!endpoints || num_endpoints <= 0) + return LIBUSB_ERROR_INVALID_PARAM; + + if (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.free_streams) + return usbi_backend.free_streams(dev_handle, endpoints, + num_endpoints); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_asyncio + * Attempts to allocate a block of persistent DMA memory suitable for transfers + * against the given device. If successful, will return a block of memory + * that is suitable for use as "buffer" in \ref libusb_transfer against this + * device. Using this memory instead of regular memory means that the host + * controller can use DMA directly into the buffer to increase performance, and + * also that transfers can no longer fail due to kernel memory fragmentation. + * + * Note that this means you should not modify this memory (or even data on + * the same cache lines) when a transfer is in progress, although it is legal + * to have several transfers going on within the same memory block. + * + * Will return NULL on failure. Many systems do not support such zero-copy + * and will always return NULL. Memory allocated with this function must be + * freed with \ref libusb_dev_mem_free. Specifically, this means that the + * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated + * with this function. + * + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 + * + * \param dev_handle a device handle + * \param length size of desired data buffer + * \returns a pointer to the newly allocated memory, or NULL on failure + */ +DEFAULT_VISIBILITY +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length) +{ + if (!usbi_atomic_load(&dev_handle->dev->attached)) + return NULL; + + if (usbi_backend.dev_mem_alloc) + return usbi_backend.dev_mem_alloc(dev_handle, length); + else + return NULL; +} + +/** \ingroup libusb_asyncio + * Free device memory allocated with libusb_dev_mem_alloc(). + * + * \param dev_handle a device handle + * \param buffer pointer to the previously allocated memory + * \param length size of previously allocated memory + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length) +{ + if (usbi_backend.dev_mem_free) + return usbi_backend.dev_mem_free(dev_handle, buffer, length); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Determine if a kernel driver is active on an interface. If a kernel driver + * is active, you cannot claim the interface, and libusb will be unable to + * perform I/O. + * + * This functionality is not available on Windows. + * + * \param dev_handle a device handle + * \param interface_number the interface to check + * \returns 0 if no kernel driver is active + * \returns 1 if a kernel driver is active + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_detach_kernel_driver() + */ +int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int 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 (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.kernel_driver_active) + return usbi_backend.kernel_driver_active(dev_handle, (uint8_t)interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * 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 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 + * not detach it and return \ref LIBUSB_ERROR_NOT_FOUND. + * + * \param dev_handle a device handle + * \param interface_number the interface to detach the driver from + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_kernel_driver_active() + */ +int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int 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 (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.detach_kernel_driver) + return usbi_backend.detach_kernel_driver(dev_handle, (uint8_t)interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Re-attach an interface's kernel driver, which was previously detached + * using libusb_detach_kernel_driver(). + * + * This functionality is not available on Windows. + * + * \param dev_handle a device handle + * \param interface_number the interface to attach the driver from + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns \ref LIBUSB_ERROR_BUSY if the driver cannot be attached because the + * interface is claimed by a program or driver + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_kernel_driver_active() + */ +int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int 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 (!usbi_atomic_load(&dev_handle->dev->attached)) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend.attach_kernel_driver) + return usbi_backend.attach_kernel_driver(dev_handle, (uint8_t)interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Enable/disable libusb's automatic kernel driver detachment. When this is + * enabled libusb will automatically detach the kernel driver on an interface + * when claiming the interface, and attach it when releasing the interface. + * + * Automatic kernel driver detachment is disabled on newly opened device + * handles by default. + * + * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER + * this function will return \ref LIBUSB_ERROR_NOT_SUPPORTED, and libusb will + * continue as if this function was never called. + * + * \param dev_handle a device handle + * \param enable whether to enable or disable auto kernel driver detachment + * + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \see libusb_claim_interface() + * \see libusb_release_interface() + * \see libusb_set_configuration() + */ +int API_EXPORTED libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable) +{ + if (!(usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) + return LIBUSB_ERROR_NOT_SUPPORTED; + + dev_handle->auto_detach_kernel_driver = enable; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_dev + * Check if the endpoint supports RAW_IO. + * \param dev_handle a device handle + * \param endpoint the endpoint to check + * + * \returns 1 if the endpoint supports RAW_IO + * \returns 0 if the endpoint does not support RAW_IO + * \returns a LIBUSB_ERROR code on failure + * + * Only endpoints using the WinUSB driver support RAW_IO, + * for all other backends/drivers this function will return 0. + * + * Fails if the interface the endpoint belongs to isn't claimed, + * \see libusb_claim_interface(). + * + * \see libusb_endpoint_set_raw_io() + * + * Since version 1.0.30, \ref LIBUSB_API_VERSION >= 0x0100010C + */ +int API_EXPORTED libusb_endpoint_supports_raw_io(libusb_device_handle* dev_handle, + uint8_t endpoint) +{ + if (usbi_backend.endpoint_supports_raw_io == NULL) + { + return 0; + } + + // If the `endpoint_supports_raw_io` function is present, these two should be too: + assert(usbi_backend.endpoint_set_raw_io != NULL); + assert(usbi_backend.get_max_raw_io_transfer_size != NULL); + + return usbi_backend.endpoint_supports_raw_io(dev_handle, endpoint); +} + +/** \ingroup libusb_dev + * Enable/disable RAW_IO for an endpoint on an open device. + * + * Only endpoints using the WinUSB driver support RAW_IO, + * for all other backends/drivers this function will return an error code. + * + * Using RAW_IO can greatly improve USB throughput by directly passing + * transfer requests to the underlying USB driver instead of queuing them + * in WinUSB. This can be particularly useful for high-throughput devices + * like cameras or oscilloscopes. + * + * Transfers submitted to the endpoint while RAW_IO is enabled will fail unless + * they adhere to the following rules : + * - The buffer length must be a multiple of the maximum endpoint packet size + * \see libusb_get_max_packet_size. + * - The buffer length must be less than or equal to the value returned by + * \ref libusb_get_max_raw_io_transfer_size. + * + * This option should not be changed when any transfer is in progress on + * the specified endpoint. + * + * Fails if the interface the endpoint belongs to isn't claimed, + * \see libusb_claim_interface(). + * + * \param dev_handle a device handle + * \param endpoint the endpoint to set RAW_IO for + * \param enable 1 to enable RAW_IO, 0 to disable it + * + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the backend does not support RAW_IO. + * \returns another LIBUSB_ERROR code on other failure + * + * \see libusb_endpoint_supports_raw_io() + * \see https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-functions-for-pipe-policy-modification + * + * Since version 1.0.30, \ref LIBUSB_API_VERSION >= 0x0100010C + */ +int API_EXPORTED libusb_endpoint_set_raw_io(libusb_device_handle* dev_handle, + uint8_t endpoint, int enable) +{ + if (!usbi_backend.endpoint_set_raw_io) + return LIBUSB_ERROR_NOT_SUPPORTED; + + return usbi_backend.endpoint_set_raw_io(dev_handle, endpoint, enable); +} + +/** \ingroup libusb_dev + * Retrieve the maximum transfer size in bytes supported for WinUSB RAW_IO + * for an inbound bulk or interrupt endpoint on an open device. + * + * Returns a maximum transfer size in bytes, or a negative error code. + * If the backend does not support WinUSB RAW_IO, returns + * \ref LIBUSB_ERROR_NOT_SUPPORTED. + * + * Fails if the interface the endpoint belongs to isn't claimed, + * \see libusb_claim_interface(). + * + * \see libusb_endpoint_set_raw_io() + * + * Since version 1.0.30, \ref LIBUSB_API_VERSION >= 0x0100010C + */ +int API_EXPORTED libusb_get_max_raw_io_transfer_size( + libusb_device_handle *dev_handle, uint8_t endpoint) +{ + if (!usbi_backend.get_max_raw_io_transfer_size) + return LIBUSB_ERROR_NOT_SUPPORTED; + + return usbi_backend.get_max_raw_io_transfer_size( + dev_handle, endpoint); +} + +/** \ingroup libusb_lib + * Deprecated. Use libusb_set_option() or libusb_init_context() instead, + * with the \ref LIBUSB_OPTION_LOG_LEVEL option. + */ +void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) +{ + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level); +} + +static void libusb_set_log_cb_internal(libusb_context *ctx, libusb_log_cb cb, + int mode) +{ +#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) +#if !defined(USE_SYSTEM_LOGGING_FACILITY) + if (mode & LIBUSB_LOG_CB_GLOBAL) + log_handler = cb; +#endif +#if !defined(ENABLE_DEBUG_LOGGING) + if (mode & LIBUSB_LOG_CB_CONTEXT) { + ctx = usbi_get_context(ctx); + ctx->log_handler = cb; + } +#else + UNUSED(ctx); +#endif +#else + UNUSED(ctx); + UNUSED(cb); + UNUSED(mode); +#endif +} + +/** \ingroup libusb_lib + * Set log handler. + * + * libusb will redirect its log messages to the provided callback function. + * libusb supports redirection of per context and global log messages. + * Log messages sent to the context will be sent to the global log handler too. + * + * If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY + * is defined then global callback function will never be called. + * 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 + * messages redirection + * \param mode mode of callback function operation. Several modes can be + * selected for a single callback function, see \ref libusb_log_cb_mode for + * a description. + * \see libusb_log_cb, libusb_log_cb_mode + */ +void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, + int mode) +{ + libusb_set_log_cb_internal(ctx, cb, mode); +} + +/** \ingroup libusb_lib + * Set an option in the library. + * + * Use this function to configure a specific option within the library. + * + * 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 + * \param option which option to set + * \param ... any required arguments for the specified option + * + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported + * on this platform + * \returns \ref LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available + */ +int API_EXPORTEDV libusb_set_option(libusb_context *ctx, + enum libusb_option option, ...) +{ + int arg = 0, r = LIBUSB_SUCCESS; + libusb_log_cb log_cb = NULL; + va_list ap; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + int is_default_context = (NULL == ctx); +#endif + + va_start(ap, option); + + 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; + } + } + if (LIBUSB_OPTION_LOG_CB == option) { + log_cb = (libusb_log_cb) va_arg(ap, libusb_log_cb); + } + + do { + if (LIBUSB_SUCCESS != r) { + break; + } + + if (option >= LIBUSB_OPTION_MAX) { + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + + 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; + } else if (LIBUSB_OPTION_LOG_CB == option) { + default_context_options[option].arg.log_cbval = log_cb; + libusb_set_log_cb_internal(NULL, log_cb, LIBUSB_LOG_CB_GLOBAL); + } + usbi_mutex_static_unlock(&default_context_lock); + } + + ctx = usbi_get_context(ctx); + if (NULL == ctx) + break; + + 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; + if (is_default_context) + usbi_atomic_store(&default_debug_level, CLAMP(arg, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG)); + } +#endif + break; + + /* Handle all backend-specific options here */ + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + if (usbi_backend.set_option) { + r = usbi_backend.set_option(ctx, option, ap); + break; + } + + r = LIBUSB_ERROR_NOT_SUPPORTED; + break; + + case LIBUSB_OPTION_LOG_CB: + libusb_set_log_cb_internal(ctx, log_cb, LIBUSB_LOG_CB_CONTEXT); + break; + + case LIBUSB_OPTION_MAX: /* unreachable */ + default: + r = LIBUSB_ERROR_INVALID_PARAM; + } + } while (0); + + va_end(ap); + + return r; +} + +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) +/* returns the log level as defined in the LIBUSB_DEBUG environment variable. + * if LIBUSB_DEBUG is not present or not a number, returns LIBUSB_LOG_LEVEL_NONE. + * value is clamped to ensure it is within the valid range of possibilities. + */ +static enum libusb_log_level get_env_debug_level(void) +{ + enum libusb_log_level level = LIBUSB_LOG_LEVEL_NONE; + const char *dbg = getenv("LIBUSB_DEBUG"); + if (dbg) { + long dbg_level = strtol(dbg, NULL, 10); + dbg_level = CLAMP(dbg_level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG); + level = (enum libusb_log_level)dbg_level; + } + return level; +} +#endif + +/** \ingroup libusb_lib + * Deprecated initialization function. Equivalent to calling libusb_init_context with no options. + * + * \see libusb_init_context + */ +int API_EXPORTED libusb_init(libusb_context **ctx) +{ + return libusb_init_context(ctx, NULL, 0); +} + +/** \ingroup libusb_lib + * Initialize libusb. This function must be called before calling any other + * libusb function. + * + * If you do not provide an output location for a context pointer, a default + * context will be created. If there was already a default context, it will + * be reused (and nothing will be initialized/reinitialized and options will + * be ignored). If num_options is 0 then options is ignored and may be NULL. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param ctx Optional output location for context pointer. + * Only valid on return code 0. + * \param options Optional array of options to set on the new context. + * \param num_options Number of elements in the options array. + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \see libusb_contexts + */ +int API_EXPORTED libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options) +{ + size_t priv_size = usbi_backend.context_priv_size; + struct libusb_context *_ctx; + int r; + + usbi_mutex_static_lock(&default_context_lock); + + 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; + } + + /* check for first init */ + usbi_mutex_static_lock(&active_contexts_lock); + if (!active_contexts_list.next) { + list_init(&active_contexts_list); + usbi_get_monotonic_time(×tamp_origin); + } + usbi_mutex_static_unlock(&active_contexts_lock); + + _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 = LIBUSB_LOG_LEVEL_NONE; + if (getenv("LIBUSB_DEBUG")) { + _ctx->debug = get_env_debug_level(); + _ctx->debug_fixed = 1; + } else if (default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { + _ctx->debug = (enum libusb_log_level)default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; + } +#endif + + 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; + } + if (LIBUSB_OPTION_LOG_CB != option) { + r = libusb_set_option(_ctx, option); + } else { + r = libusb_set_option(_ctx, option, default_context_options[option].arg.log_cbval); + } + if (LIBUSB_SUCCESS != r) + goto err_free_ctx; + } + + /* apply any options provided by the user */ + for (int i = 0 ; i < num_options ; ++i) { + switch(options[i].option) { + case LIBUSB_OPTION_LOG_CB: + r = libusb_set_option(_ctx, options[i].option, options[i].value.log_cbval); + break; + + case LIBUSB_OPTION_LOG_LEVEL: + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + case LIBUSB_OPTION_MAX: + default: + r = libusb_set_option(_ctx, options[i].option, options[i].value.ival); + } + 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; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif + usbi_dbg(usbi_default_context, "created default context"); + } + + 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); + + r = usbi_io_init(_ctx); + if (r < 0) + goto err_free_ctx; + + usbi_mutex_static_lock(&active_contexts_lock); + list_add(&_ctx->list, &active_contexts_list); + usbi_mutex_static_unlock(&active_contexts_lock); + + if (usbi_backend.init) { + r = usbi_backend.init(_ctx); + if (r) + goto err_io_exit; + } + + /* Initialize hotplug after the initial enumeration is done. */ + usbi_hotplug_init(_ctx); + + if (ctx) { + *ctx = _ctx; + + if (!usbi_fallback_context) { +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + if (usbi_atomic_load(&default_debug_level) == -1) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif + usbi_fallback_context = _ctx; + usbi_dbg(usbi_fallback_context, "installing new context as implicit default"); + } + } + + usbi_mutex_static_unlock(&default_context_lock); + + return 0; + +err_io_exit: + usbi_mutex_static_lock(&active_contexts_lock); + list_del(&_ctx->list); + usbi_mutex_static_unlock(&active_contexts_lock); + + 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_destroy(&_ctx->open_devs_lock); + usbi_mutex_destroy(&_ctx->usb_devs_lock); + + free(_ctx); + + usbi_mutex_static_unlock(&default_context_lock); + + return r; +} + +/** \ingroup libusb_lib + * Deinitialize libusb. Should be called after closing all open devices and + * before your application terminates. + * \param ctx the context to deinitialize, or NULL for the default context + */ +void API_EXPORTED libusb_exit(libusb_context *ctx) +{ + struct libusb_context *_ctx; + struct libusb_device *dev; + + usbi_mutex_static_lock(&default_context_lock); + + /* if working with default context, only actually do the deinitialization + * if we're the last user */ + if (!ctx) { + if (!usbi_default_context) { + usbi_dbg(ctx, "no default context, not initialized?"); + usbi_mutex_static_unlock(&default_context_lock); + return; + } + + if (--default_context_refcnt > 0) { + usbi_dbg(ctx, "not destroying default context"); + usbi_mutex_static_unlock(&default_context_lock); + return; + } + + usbi_dbg(ctx, "destroying default context"); + _ctx = usbi_default_context; + } else { + usbi_dbg(ctx, " "); + _ctx = ctx; + } + + usbi_mutex_static_lock(&active_contexts_lock); + list_del(&_ctx->list); + usbi_mutex_static_unlock(&active_contexts_lock); + + /* Exit hotplug before backend dependency */ + usbi_hotplug_exit(_ctx); + + if (usbi_backend.exit) + usbi_backend.exit(_ctx); + + if (!ctx) + usbi_default_context = NULL; + if (ctx == usbi_fallback_context) + usbi_fallback_context = NULL; + + usbi_mutex_static_unlock(&default_context_lock); + + /* Don't bother with locking after this point because unless there is + * an application bug, nobody will be accessing the context. */ + + usbi_io_exit(_ctx); + + 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 + * Check at runtime if the loaded library has a given capability. + * This call should be performed after \ref libusb_init_context(), to ensure the + * backend has updated its capability set. + * + * \param capability the \ref libusb_capability to check for + * \returns nonzero if the running library has the capability, 0 otherwise + */ +int API_EXPORTED libusb_has_capability(uint32_t capability) +{ + switch (capability) { + case LIBUSB_CAP_HAS_CAPABILITY: + return 1; + case LIBUSB_CAP_HAS_HOTPLUG: + return !(usbi_backend.get_device_list); + case LIBUSB_CAP_HAS_HID_ACCESS: + return (usbi_backend.caps & USBI_CAP_HAS_HID_ACCESS); + case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: + return (usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER); + } + return 0; +} + +#ifdef ENABLE_LOGGING + +/* this is defined in libusbi.h if needed */ +#ifdef LIBUSB_PRINTF_WIN32 +/* + * Prior to VS2015, Microsoft did not provide the snprintf() function and + * provided a vsnprintf() that did not guarantee NUL-terminated output. + * Microsoft did provide a _snprintf() function, but again it did not + * guarantee NULL-terminated output. + * + * The below implementations guarantee NUL-terminated output and are + * C99 compliant. + */ + +int usbi_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = usbi_vsnprintf(str, size, format, args); + va_end(args); + + return ret; +} + +int usbi_vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + int ret; + + ret = _vsnprintf(str, size, format, args); + if (ret < 0 || ret == (int)size) { + /* Output is truncated, ensure buffer is NUL-terminated and + * determine how many characters would have been written. */ + str[size - 1] = '\0'; + if (ret < 0) + ret = _vsnprintf(NULL, 0, format, args); + } + + return ret; +} +#endif /* LIBUSB_PRINTF_WIN32 */ + +static void log_str(enum libusb_log_level level, const char *str) +{ +#if defined(USE_SYSTEM_LOGGING_FACILITY) +#if defined(__ANDROID__) + int priority; + switch (level) { + case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */ + case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break; + case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break; + case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break; + case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break; + default: priority = ANDROID_LOG_UNKNOWN; + } + __android_log_write(priority, "libusb", str); +#elif defined(_WIN32) + UNUSED(level); + OutputDebugStringA(str); +#elif defined(HAVE_SYSLOG) + int syslog_level; + switch (level) { + case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */ + case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break; + case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break; + case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break; + case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break; + default: syslog_level = LOG_INFO; + } + syslog(syslog_level, "%s", str); +#else /* All of gcc, Clang, Xcode seem to use #warning */ +#warning System logging is not supported on this platform. Logging to stderr will be used instead. + UNUSED(level); + fputs(str, stderr); +#endif +#else + /* Global log handler */ + if (log_handler) + log_handler(NULL, level, str); + else + fputs(str, stderr); +#endif /* USE_SYSTEM_LOGGING_FACILITY */ +} + +static void log_v(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, va_list args) +{ + const char *prefix; + char buf[USBI_MAX_LOG_LEN]; + int global_debug, header_len, text_len; + static int has_debug_header_been_displayed = 0; + +#ifdef ENABLE_DEBUG_LOGGING + global_debug = 1; + UNUSED(ctx); +#else + enum libusb_log_level ctx_level; + long default_level_value; + + if (ctx) { + ctx_level = ctx->debug; + } else { + default_level_value = usbi_atomic_load(&default_debug_level); + ctx_level = default_level_value < 0 ? get_env_debug_level() : (enum libusb_log_level)default_level_value; + } + + if (ctx_level < level) + return; + + global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG); +#endif + + switch (level) { + case LIBUSB_LOG_LEVEL_NONE: /* Impossible, but keeps compiler happy */ + return; + case LIBUSB_LOG_LEVEL_ERROR: + prefix = "error"; + break; + case LIBUSB_LOG_LEVEL_WARNING: + prefix = "warning"; + break; + case LIBUSB_LOG_LEVEL_INFO: + prefix = "info"; + break; + case LIBUSB_LOG_LEVEL_DEBUG: + prefix = "debug"; + break; + default: + prefix = "unknown"; + break; + } + + if (global_debug) { + struct timespec timestamp; + + if (!has_debug_header_been_displayed) { + has_debug_header_been_displayed = 1; + log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] " USBI_LOG_LINE_END); + log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END); + } + + usbi_get_monotonic_time(×tamp); + TIMESPEC_SUB(×tamp, ×tamp_origin, ×tamp); + + header_len = snprintf(buf, sizeof(buf), + "[%2ld.%06ld] [%08lx] libusb: %s [%s] ", + (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000L), usbi_get_tid(), prefix, function); + } else { + header_len = snprintf(buf, sizeof(buf), + "libusb: %s [%s] ", prefix, function); + } + + if (header_len < 0 || header_len >= (int)sizeof(buf)) { + /* Somehow snprintf() failed to write to the buffer, + * remove the header so something useful is output. */ + header_len = 0; + } + + text_len = vsnprintf(buf + header_len, sizeof(buf) - (size_t)header_len, + format, args); + if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) { + /* Truncated log output. On some platforms a -1 return value means + * that the output was truncated. */ + text_len = (int)sizeof(buf) - header_len; + } + if (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END) >= (int)sizeof(buf)) { + /* Need to truncate the text slightly to fit on the terminator. */ + text_len -= (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END)) - (int)sizeof(buf); + } + strcpy(buf + header_len + text_len, USBI_LOG_LINE_END); + + log_str(level, buf); + + /* Per-context log handler */ +#ifndef ENABLE_DEBUG_LOGGING + if (ctx && ctx->log_handler) + ctx->log_handler(ctx, level, buf); +#endif +} + +void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, ...) +{ + va_list args; + + va_start(args, format); + log_v(ctx, level, function, format, args); + va_end(args); +} + +#endif /* ENABLE_LOGGING */ + +/** \ingroup libusb_misc + * Returns a constant NULL-terminated string with the ASCII name of a libusb + * error or transfer status code. The caller must not free() the returned + * string. + * + * \param error_code The \ref libusb_error or libusb_transfer_status code to + * return the name of. + * \returns The error name, or the string **UNKNOWN** if the value of + * error_code is not a known error / status code. + */ +DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code) +{ + switch (error_code) { + case LIBUSB_ERROR_IO: + return "LIBUSB_ERROR_IO"; + case LIBUSB_ERROR_INVALID_PARAM: + return "LIBUSB_ERROR_INVALID_PARAM"; + case LIBUSB_ERROR_ACCESS: + return "LIBUSB_ERROR_ACCESS"; + case LIBUSB_ERROR_NO_DEVICE: + return "LIBUSB_ERROR_NO_DEVICE"; + case LIBUSB_ERROR_NOT_FOUND: + return "LIBUSB_ERROR_NOT_FOUND"; + case LIBUSB_ERROR_BUSY: + return "LIBUSB_ERROR_BUSY"; + case LIBUSB_ERROR_TIMEOUT: + return "LIBUSB_ERROR_TIMEOUT"; + case LIBUSB_ERROR_OVERFLOW: + return "LIBUSB_ERROR_OVERFLOW"; + case LIBUSB_ERROR_PIPE: + return "LIBUSB_ERROR_PIPE"; + case LIBUSB_ERROR_INTERRUPTED: + return "LIBUSB_ERROR_INTERRUPTED"; + case LIBUSB_ERROR_NO_MEM: + return "LIBUSB_ERROR_NO_MEM"; + case LIBUSB_ERROR_NOT_SUPPORTED: + return "LIBUSB_ERROR_NOT_SUPPORTED"; + case LIBUSB_ERROR_OTHER: + return "LIBUSB_ERROR_OTHER"; + + case LIBUSB_TRANSFER_ERROR: + return "LIBUSB_TRANSFER_ERROR"; + case LIBUSB_TRANSFER_TIMED_OUT: + return "LIBUSB_TRANSFER_TIMED_OUT"; + case LIBUSB_TRANSFER_CANCELLED: + return "LIBUSB_TRANSFER_CANCELLED"; + case LIBUSB_TRANSFER_STALL: + return "LIBUSB_TRANSFER_STALL"; + case LIBUSB_TRANSFER_NO_DEVICE: + return "LIBUSB_TRANSFER_NO_DEVICE"; + case LIBUSB_TRANSFER_OVERFLOW: + return "LIBUSB_TRANSFER_OVERFLOW"; + + case 0: + return "LIBUSB_SUCCESS / LIBUSB_TRANSFER_COMPLETED"; + default: + return "**UNKNOWN**"; + } +} + +/** \ingroup libusb_misc + * Returns a pointer to const struct libusb_version with the version + * (major, minor, micro, nano and rc) of the running library. + */ +DEFAULT_VISIBILITY +const struct libusb_version * LIBUSB_CALL libusb_get_version(void) +{ + return &libusb_version_internal; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/descriptor.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/descriptor.c new file mode 100644 index 00000000..15130e65 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/descriptor.c @@ -0,0 +1,1707 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * USB descriptor handling functions for libusb + * Copyright © 2007 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include + +#define DESC_HEADER_LENGTH 2 + +/** @defgroup libusb_desc USB descriptors + * This page details how to examine the various standard USB descriptors + * for detected devices + */ + +static inline uint16_t ReadLittleEndian16(const uint8_t p[2]) +{ + return (uint16_t)((uint16_t)p[1] << 8 | + (uint16_t)p[0]); +} + +static inline uint32_t ReadLittleEndian32(const uint8_t p[4]) +{ + return ((uint32_t)p[3] << 24 | + (uint32_t)p[2] << 16 | + (uint32_t)p[1] << 8 | + (uint32_t)p[0]); +} + +static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint) +{ + free((void *)endpoint->extra); +} + +static int parse_endpoint(struct libusb_context *ctx, + struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size) +{ + const struct usbi_descriptor_header *header; + const uint8_t *begin; + void *extra; + int parsed = 0; + + if (size < DESC_HEADER_LENGTH) { + usbi_err(ctx, "short endpoint descriptor read %d/%d", + size, DESC_HEADER_LENGTH); + return LIBUSB_ERROR_IO; + } + + header = (const struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + header->bDescriptorType, LIBUSB_DT_ENDPOINT); + return parsed; + } else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) { + usbi_err(ctx, "invalid endpoint bLength (%u)", header->bLength); + return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_warn(ctx, "short endpoint descriptor read %d/%u", + size, header->bLength); + return parsed; + } + + endpoint->bLength = buffer[0]; + endpoint->bDescriptorType = buffer[1]; + endpoint->bEndpointAddress = buffer[2]; + endpoint->bmAttributes = buffer[3]; + endpoint->wMaxPacketSize = ReadLittleEndian16(&buffer[4]); + endpoint->bInterval = buffer[6]; + if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE) { + endpoint->bRefresh = buffer[7]; + endpoint->bSynchAddress = buffer[8]; + } + + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; + + /* Skip over the rest of the Class Specific or Vendor Specific */ + /* descriptors */ + begin = buffer; + while (size >= DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid extra ep desc len (%u)", + header->bLength); + return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_warn(ctx, "short extra ep desc read %d/%u", + size, header->bLength); + return parsed; + } + + /* If we find another "proper" descriptor then we're done */ + if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) + break; + + usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; + } + + /* Copy any unknown descriptors into a storage area for drivers */ + /* to later parse */ + ptrdiff_t len = buffer - begin; + if (len <= 0) + return parsed; + + extra = malloc((size_t)len); + if (!extra) + return LIBUSB_ERROR_NO_MEM; + + memcpy(extra, begin, (size_t)len); + endpoint->extra = extra; + endpoint->extra_length = (int)len; + + return parsed; +} + +static void clear_interface(struct libusb_interface *usb_interface) +{ + int i; + + if (usb_interface->altsetting) { + for (i = 0; i < usb_interface->num_altsetting; i++) { + struct libusb_interface_descriptor *ifp = + (struct libusb_interface_descriptor *) + usb_interface->altsetting + i; + + free((void *)ifp->extra); + ifp->extra = NULL; + ifp->extra_length = 0; + if (ifp->endpoint) { + uint8_t j; + + for (j = 0; j < ifp->bNumEndpoints; j++) + clear_endpoint((struct libusb_endpoint_descriptor *) + ifp->endpoint + j); + } + free((void *)ifp->endpoint); + ifp->endpoint = NULL; + ifp->bNumEndpoints = 0; + } + } + free((void *)usb_interface->altsetting); + usb_interface->altsetting = NULL; + usb_interface->num_altsetting = 0; +} + +static int parse_interface(libusb_context *ctx, + struct libusb_interface *usb_interface, const uint8_t *buffer, int size) +{ + int r; + int parsed = 0; + int interface_number = -1; + const struct usbi_descriptor_header *header; + const struct usbi_interface_descriptor *if_desc; + struct libusb_interface_descriptor *ifp; + const uint8_t *begin; + + while (size >= LIBUSB_DT_INTERFACE_SIZE) { + struct libusb_interface_descriptor *altsetting; + + altsetting = realloc((void *)usb_interface->altsetting, + sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1)); + if (!altsetting) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + usb_interface->altsetting = altsetting; + + ifp = altsetting + usb_interface->num_altsetting; + ifp->bLength = buffer[0]; + ifp->bDescriptorType = buffer[1]; + ifp->bInterfaceNumber = buffer[2]; + ifp->bAlternateSetting = buffer[3]; + ifp->bNumEndpoints = buffer[4]; + ifp->bInterfaceClass = buffer[5]; + ifp->bInterfaceSubClass = buffer[6]; + ifp->bInterfaceProtocol = buffer[7]; + ifp->iInterface = buffer[8]; + if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + ifp->bDescriptorType, LIBUSB_DT_INTERFACE); + return parsed; + } else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) { + usbi_err(ctx, "invalid interface bLength (%u)", + ifp->bLength); + r = LIBUSB_ERROR_IO; + goto err; + } else if (ifp->bLength > size) { + usbi_warn(ctx, "short intf descriptor read %d/%u", + size, ifp->bLength); + return parsed; + } else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { + usbi_err(ctx, "too many endpoints (%u)", ifp->bNumEndpoints); + r = LIBUSB_ERROR_IO; + goto err; + } + + usb_interface->num_altsetting++; + ifp->extra = NULL; + ifp->extra_length = 0; + ifp->endpoint = NULL; + + if (interface_number == -1) + interface_number = ifp->bInterfaceNumber; + + /* Skip over the interface */ + buffer += ifp->bLength; + parsed += ifp->bLength; + size -= ifp->bLength; + + begin = buffer; + + /* Skip over any interface, class or vendor descriptors */ + while (size >= DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, + "invalid extra intf desc len (%u)", + header->bLength); + r = LIBUSB_ERROR_IO; + goto err; + } else if (header->bLength > size) { + usbi_warn(ctx, + "short extra intf desc read %d/%u", + size, header->bLength); + /* Keep the invariant: bNumEndpoints > 0 implies + * endpoint != NULL. The endpoint array isn't + * allocated yet on this early return. */ + ifp->bNumEndpoints = 0; + return parsed; + } + + /* If we find another "proper" descriptor then we're done */ + if (header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) + break; + + buffer += header->bLength; + parsed += header->bLength; + size -= header->bLength; + } + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + ptrdiff_t len = buffer - begin; + if (len > 0) { + void *extra = malloc((size_t)len); + + if (!extra) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + + memcpy(extra, begin, (size_t)len); + ifp->extra = extra; + ifp->extra_length = (int)len; + } + + if (ifp->bNumEndpoints > 0) { + struct libusb_endpoint_descriptor *endpoint; + uint8_t i; + + endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint)); + if (!endpoint) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + + ifp->endpoint = endpoint; + for (i = 0; i < ifp->bNumEndpoints; i++) { + r = parse_endpoint(ctx, endpoint + i, buffer, size); + if (r < 0) + goto err; + if (r == 0) { + ifp->bNumEndpoints = i; + break; + } + + buffer += r; + parsed += r; + size -= r; + } + } + + /* We check to see if it's an alternate to this one */ + if_desc = (const struct usbi_interface_descriptor *)buffer; + if (size < LIBUSB_DT_INTERFACE_SIZE || + if_desc->bDescriptorType != LIBUSB_DT_INTERFACE || + if_desc->bInterfaceNumber != interface_number) + return parsed; + } + + return parsed; +err: + clear_interface(usb_interface); + return r; +} + +static void clear_configuration(struct libusb_config_descriptor *config) +{ + uint8_t i; + + if (config->interface) { + for (i = 0; i < config->bNumInterfaces; i++) + clear_interface((struct libusb_interface *) + config->interface + i); + } + free((void *)config->interface); + config->interface = NULL; + config->bNumInterfaces = 0; + free((void *)config->extra); + config->extra = NULL; + config->extra_length = 0; +} + +static int parse_configuration(struct libusb_context *ctx, + struct libusb_config_descriptor *config, const uint8_t *buffer, int size) +{ + uint8_t i; + int r; + const struct usbi_descriptor_header *header; + struct libusb_interface *usb_interface; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + config->bLength = buffer[0]; + config->bDescriptorType = buffer[1]; + config->wTotalLength = ReadLittleEndian16(&buffer[2]); + config->bNumInterfaces = buffer[4]; + config->bConfigurationValue = buffer[5]; + config->iConfiguration = buffer[6]; + config->bmAttributes = buffer[7]; + config->MaxPower = buffer[8]; + if (config->bDescriptorType != LIBUSB_DT_CONFIG) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + config->bDescriptorType, LIBUSB_DT_CONFIG); + return LIBUSB_ERROR_IO; + } else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid config bLength (%u)", config->bLength); + return LIBUSB_ERROR_IO; + } else if (config->bLength > size) { + usbi_err(ctx, "short config descriptor read %d/%u", + size, config->bLength); + return LIBUSB_ERROR_IO; + } else if (config->bNumInterfaces > USB_MAXINTERFACES) { + usbi_err(ctx, "too many interfaces (%u)", config->bNumInterfaces); + return LIBUSB_ERROR_IO; + } + + usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface)); + if (!usb_interface) + return LIBUSB_ERROR_NO_MEM; + + config->interface = usb_interface; + + buffer += config->bLength; + size -= config->bLength; + + for (i = 0; i < config->bNumInterfaces; i++) { + const uint8_t *begin; + + /* Skip over the rest of the Class Specific or Vendor */ + /* Specific descriptors */ + begin = buffer; + while (size >= DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, + "invalid extra config desc len (%u)", + header->bLength); + r = LIBUSB_ERROR_IO; + goto err; + } else if (header->bLength > size) { + usbi_warn(ctx, + "short extra config desc read %d/%u", + size, header->bLength); + config->bNumInterfaces = i; + return size; + } + + /* If we find another "proper" descriptor then we're done */ + if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) + break; + + usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); + buffer += header->bLength; + size -= header->bLength; + } + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + ptrdiff_t len = buffer - begin; + if (len > 0) { + uint8_t *extra = realloc((void *)config->extra, + (size_t)(config->extra_length) + (size_t)len); + + if (!extra) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + + memcpy(extra + config->extra_length, begin, (size_t)len); + config->extra = extra; + config->extra_length += (int)len; + } + + r = parse_interface(ctx, usb_interface + i, buffer, size); + if (r < 0) + goto err; + if (r == 0) { + config->bNumInterfaces = i; + break; + } + + buffer += r; + size -= r; + } + + return size; + +err: + clear_configuration(config); + return r; +} + +static int raw_desc_to_config(struct libusb_context *ctx, + const uint8_t *buf, int size, struct libusb_config_descriptor **config) +{ + struct libusb_config_descriptor *_config = calloc(1, sizeof(*_config)); + int r; + + if (!_config) + return LIBUSB_ERROR_NO_MEM; + + r = parse_configuration(ctx, _config, buf, size); + if (r < 0) { + usbi_err(ctx, "parse_configuration failed with error %d", r); + free(_config); + return r; + } else if (r > 0) { + usbi_warn(ctx, "still %d bytes of descriptor data left", r); + } + + *config = _config; + return LIBUSB_SUCCESS; +} + +static int get_active_config_descriptor(struct libusb_device *dev, + uint8_t *buffer, size_t size) +{ + int r = usbi_backend.get_active_config_descriptor(dev, buffer, size); + + if (r < 0) + return r; + + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } else if (r != (int)size) { + usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, (int)size); + } + + return r; +} + +static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, + uint8_t *buffer, size_t size) +{ + int r = usbi_backend.get_config_descriptor(dev, config_idx, buffer, size); + + if (r < 0) + return r; + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } else if (r != (int)size) { + usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, (int)size); + } + + return r; +} + +/** \ingroup libusb_desc + * Get the USB device descriptor for a given device. + * + * This is a non-blocking function; the device descriptor is cached in memory. + * + * Note since libusb-1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, this + * function always succeeds. + * + * \param dev the device + * \param desc output location for the descriptor data + * \returns 0 on success or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc) +{ + 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; + return 0; +} + +/** \ingroup libusb_desc + * Get the USB configuration descriptor for the currently active configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_config_descriptor + */ +int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_active_config_descriptor(dev, buf, config_len); + if (r >= 0) + r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get a USB configuration descriptor based on its index. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config_index the index of the configuration you wish to retrieve + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_config_descriptor() + * \see libusb_get_config_descriptor_by_value() + */ +int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + usbi_dbg(DEVICE_CTX(dev), "index %u", config_index); + if (config_index >= dev->device_descriptor.bNumConfigurations) + return LIBUSB_ERROR_NOT_FOUND; + + r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_config_descriptor(dev, config_index, buf, config_len); + if (r >= 0) + r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get a USB configuration descriptor with a specific bConfigurationValue. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param bConfigurationValue the bConfigurationValue of the configuration you + * wish to retrieve + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_config_descriptor() + * \see libusb_get_config_descriptor() + */ +int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config) +{ + uint8_t idx; + int r; + + if (usbi_backend.get_config_descriptor_by_value) { + void *buf; + + r = usbi_backend.get_config_descriptor_by_value(dev, + bConfigurationValue, &buf); + if (r < 0) + return r; + + return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); + } + + usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue); + for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { + union usbi_config_desc_buf _config; + + r = get_config_descriptor(dev, idx, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + if (_config.desc.bConfigurationValue == bConfigurationValue) + return libusb_get_config_descriptor(dev, idx, config); + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +/** \ingroup libusb_desc + * Free a configuration descriptor obtained from + * libusb_get_active_config_descriptor() or libusb_get_config_descriptor(). + * It is safe to call this function with a NULL config parameter, in which + * case the function simply returns. + * + * \param config the configuration descriptor to free + */ +void API_EXPORTED libusb_free_config_descriptor( + struct libusb_config_descriptor *config) +{ + if (!config) + return; + + clear_configuration(config); + free(config); +} + +/** \ingroup libusb_desc + * Get an endpoints superspeed endpoint companion descriptor (if any) + * + * \param ctx the context to operate on, or NULL for the default context + * \param endpoint endpoint descriptor from which to get the superspeed + * endpoint companion descriptor + * \param ep_comp output location for the superspeed endpoint companion + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_endpoint_companion_descriptor() after use. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( + libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp) +{ + const struct usbi_descriptor_header *header; + const uint8_t *buffer = endpoint->extra; + int size = endpoint->extra_length; + + *ep_comp = NULL; + + while (size >= DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid descriptor length %u", + header->bLength); + return LIBUSB_ERROR_IO; + } + buffer += header->bLength; + size -= header->bLength; + continue; + } else if (header->bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { + usbi_err(ctx, "invalid ss-ep-comp-desc length %u", + header->bLength); + return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_err(ctx, "short ss-ep-comp-desc read %d/%u", + size, header->bLength); + return LIBUSB_ERROR_IO; + } + + *ep_comp = malloc(sizeof(**ep_comp)); + if (!*ep_comp) + return LIBUSB_ERROR_NO_MEM; + (*ep_comp)->bLength = buffer[0]; + (*ep_comp)->bDescriptorType = buffer[1]; + (*ep_comp)->bMaxBurst = buffer[2]; + (*ep_comp)->bmAttributes = buffer[3]; + (*ep_comp)->wBytesPerInterval = ReadLittleEndian16(&buffer[4]); + return LIBUSB_SUCCESS; + } + return LIBUSB_ERROR_NOT_FOUND; +} + +/** \ingroup libusb_desc + * Free a superspeed endpoint companion descriptor obtained from + * libusb_get_ss_endpoint_companion_descriptor(). + * It is safe to call this function with a NULL ep_comp parameter, in which + * case the function simply returns. + * + * \param ep_comp the superspeed endpoint companion descriptor to free + */ +void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp) +{ + free(ep_comp); +} + +static int parse_bos(struct libusb_context *ctx, + struct libusb_bos_descriptor **bos, + const uint8_t *buffer, int size) +{ + struct libusb_bos_descriptor *_bos; + const struct usbi_bos_descriptor *bos_desc; + const struct usbi_descriptor_header *header; + uint8_t i; + + if (size < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "short bos descriptor read %d/%d", + size, LIBUSB_DT_BOS_SIZE); + return LIBUSB_ERROR_IO; + } + + bos_desc = (const struct usbi_bos_descriptor *)buffer; + if (bos_desc->bDescriptorType != LIBUSB_DT_BOS) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + bos_desc->bDescriptorType, LIBUSB_DT_BOS); + return LIBUSB_ERROR_IO; + } else if (bos_desc->bLength < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "invalid bos bLength (%u)", bos_desc->bLength); + return LIBUSB_ERROR_IO; + } else if (bos_desc->bLength > size) { + usbi_err(ctx, "short bos descriptor read %d/%u", + size, bos_desc->bLength); + return LIBUSB_ERROR_IO; + } + + _bos = calloc(1, sizeof(*_bos) + (bos_desc->bNumDeviceCaps * sizeof(void *))); + if (!_bos) + return LIBUSB_ERROR_NO_MEM; + + _bos->bLength = buffer[0]; + _bos->bDescriptorType = buffer[1]; + _bos->wTotalLength = ReadLittleEndian16(&buffer[2]); + _bos->bNumDeviceCaps = buffer[4]; + buffer += _bos->bLength; + size -= _bos->bLength; + + /* Get the device capability descriptors */ + for (i = 0; i < _bos->bNumDeviceCaps; i++) { + if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%d", + size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE); + break; + } + header = (const struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { + usbi_warn(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + header->bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); + break; + } else if (header->bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "invalid dev-cap bLength (%u)", + header->bLength); + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%u", + size, header->bLength); + break; + } + + _bos->dev_capability[i] = malloc(header->bLength); + if (!_bos->dev_capability[i]) { + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_NO_MEM; + } + memcpy(_bos->dev_capability[i], buffer, header->bLength); + buffer += header->bLength; + size -= header->bLength; + } + _bos->bNumDeviceCaps = i; + *bos = _bos; + + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get a Binary Object Store (BOS) descriptor + * This is a BLOCKING function, which will send requests to the device. + * + * \param dev_handle the handle of an open libusb device + * \param bos output location for the BOS descriptor. Only valid if 0 was returned. + * Must be freed with \ref libusb_free_bos_descriptor() after use. + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos) +{ + union usbi_bos_desc_buf _bos; + 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(ctx, "failed to read BOS (%d)", r); + return r; + } + if (r < LIBUSB_DT_BOS_SIZE) { + 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(ctx, "found BOS descriptor: size %u bytes, %u capabilities", + bos_len, _bos.desc.bNumDeviceCaps); + bos_data = calloc(1, bos_len); + if (!bos_data) + return LIBUSB_ERROR_NO_MEM; + + r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len); + if (r >= 0) { + if (r != (int)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(ctx, "failed to read BOS (%d)", r); + } + + free(bos_data); + return r; +} + +/** \ingroup libusb_desc + * Free a BOS descriptor obtained from libusb_get_bos_descriptor(). + * It is safe to call this function with a NULL bos parameter, in which + * case the function simply returns. + * + * \param bos the BOS descriptor to free + */ +void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) +{ + uint8_t i; + + if (!bos) + return; + + for (i = 0; i < bos->bNumDeviceCaps; i++) + free(bos->dev_capability[i]); + free(bos); +} + +/** \ingroup libusb_desc + * Get an USB 2.0 Extension descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_bos_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION + * \param usb_2_0_extension output location for the USB 2.0 Extension + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_usb_2_0_extension_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) +{ + struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_USB_2_0_EXTENSION); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE); + return LIBUSB_ERROR_IO; + } + + _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension)); + if (!_usb_2_0_extension) + return LIBUSB_ERROR_NO_MEM; + + _usb_2_0_extension->bLength = dev_cap->bLength; + _usb_2_0_extension->bDescriptorType = dev_cap->bDescriptorType; + _usb_2_0_extension->bDevCapabilityType = dev_cap->bDevCapabilityType; + _usb_2_0_extension->bmAttributes = ReadLittleEndian32(dev_cap->dev_capability_data); + + *usb_2_0_extension = _usb_2_0_extension; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a USB 2.0 Extension descriptor obtained from + * libusb_get_usb_2_0_extension_descriptor(). + * It is safe to call this function with a NULL usb_2_0_extension parameter, + * in which case the function simply returns. + * + * \param usb_2_0_extension the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension) +{ + free(usb_2_0_extension); +} + +/** \ingroup libusb_desc + * Get a SuperSpeed USB Device Capability descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_bos_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * \param ss_usb_device_cap output location for the SuperSpeed USB Device + * Capability descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_usb_device_capability_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap) +{ + struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE); + return LIBUSB_ERROR_IO; + } + + _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap)); + if (!_ss_usb_device_cap) + return LIBUSB_ERROR_NO_MEM; + + _ss_usb_device_cap->bLength = dev_cap->bLength; + _ss_usb_device_cap->bDescriptorType = dev_cap->bDescriptorType; + _ss_usb_device_cap->bDevCapabilityType = dev_cap->bDevCapabilityType; + _ss_usb_device_cap->bmAttributes = dev_cap->dev_capability_data[0]; + _ss_usb_device_cap->wSpeedSupported = ReadLittleEndian16(&dev_cap->dev_capability_data[1]); + _ss_usb_device_cap->bFunctionalitySupport = dev_cap->dev_capability_data[3]; + _ss_usb_device_cap->bU1DevExitLat = dev_cap->dev_capability_data[4]; + _ss_usb_device_cap->bU2DevExitLat = ReadLittleEndian16(&dev_cap->dev_capability_data[5]); + + *ss_usb_device_cap = _ss_usb_device_cap; + return LIBUSB_SUCCESS; +} + +/// @cond DEV +/** \internal \ingroup libusb_desc + * We use this private struct only to parse a SuperSpeedPlus device capability + * descriptor according to section 9.6.2.5 of the USB 3.1 specification. + * We don't expose it. + */ +struct internal_ssplus_capability_descriptor { + /** The length of the descriptor. Must be equal to LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE */ + uint8_t bLength; + + /** The type of the descriptor */ + uint8_t bDescriptorType; + + /** Must be equal to LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY */ + uint8_t bDevCapabilityType; + + /** Unused */ + uint8_t bReserved; + + /** Contains the number of SublinkSpeedIDs */ + uint32_t bmAttributes; + + /** Contains the ssid, minRxLaneCount, and minTxLaneCount */ + uint16_t wFunctionalitySupport; + + /** Unused */ + uint16_t wReserved; +}; +/// @endcond + +/** \ingroup libusb_desc + * Get a SuperSpeedPlus USB Device Capability descriptor + * + * Since version 1.0.28, \ref LIBUSB_API_VERSION >= 0x0100010B + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_bos_type::LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY + * LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY + * \param ssplus_usb_device_cap output location for the SuperSpeedPlus USB Device + * Capability descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ssplus_usb_device_capability_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ssplus_usb_device_capability_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ssplus_usb_device_capability_descriptor **ssplus_usb_device_cap) +{ + struct libusb_ssplus_usb_device_capability_descriptor *_ssplus_cap; + + /* Use a private struct to reuse our descriptor parsing system. */ + struct internal_ssplus_capability_descriptor parsedDescriptor; + + /* Some size/type checks to make sure everything is in order */ + if (dev_cap->bDevCapabilityType != LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE); + return LIBUSB_ERROR_IO; + } + + const uint8_t* dev_capability_data = dev_cap->dev_capability_data; + parsedDescriptor.bLength = dev_cap->bLength; + parsedDescriptor.bDescriptorType = dev_cap->bDescriptorType; + parsedDescriptor.bDevCapabilityType = dev_cap->bDevCapabilityType; + parsedDescriptor.bReserved = dev_capability_data[0]; + parsedDescriptor.bmAttributes = ReadLittleEndian32(&dev_capability_data[1]); + parsedDescriptor.wFunctionalitySupport = ReadLittleEndian16(&dev_capability_data[5]); + parsedDescriptor.wReserved = ReadLittleEndian16(&dev_capability_data[7]); + + uint8_t numSublikSpeedAttributes = (parsedDescriptor.bmAttributes & 0xF) + 1; + _ssplus_cap = malloc(sizeof(struct libusb_ssplus_usb_device_capability_descriptor) + (numSublikSpeedAttributes * sizeof(struct libusb_ssplus_sublink_attribute))); + if (!_ssplus_cap) + return LIBUSB_ERROR_NO_MEM; + + /* Parse bmAttributes */ + _ssplus_cap->numSublinkSpeedAttributes = numSublikSpeedAttributes; + _ssplus_cap->numSublinkSpeedIDs = ((parsedDescriptor.bmAttributes & 0xF0) >> 4) + 1; + + /* Parse wFunctionalitySupport */ + _ssplus_cap->ssid = parsedDescriptor.wFunctionalitySupport & 0xF; + _ssplus_cap->minRxLaneCount = (parsedDescriptor.wFunctionalitySupport & 0x0F00) >> 8; + _ssplus_cap->minTxLaneCount = (parsedDescriptor.wFunctionalitySupport & 0xF000) >> 12; + + /* Check that we have enough to read all the sublink attributes */ + if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE + (_ssplus_cap->numSublinkSpeedAttributes * sizeof(uint32_t))) { + free(_ssplus_cap); + usbi_err(ctx, "short ssplus capability descriptor, unable to read sublinks: Not enough data"); + return LIBUSB_ERROR_IO; + } + + /* Read the attributes */ + uint8_t* base = ((uint8_t*)dev_cap) + LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE; + for(uint8_t i = 0 ; i < _ssplus_cap->numSublinkSpeedAttributes ; i++) { + uint32_t attr = ReadLittleEndian32(base + (i * sizeof(uint32_t))); + _ssplus_cap->sublinkSpeedAttributes[i].ssid = attr & 0x0f; + _ssplus_cap->sublinkSpeedAttributes[i].mantissa = attr >> 16; + _ssplus_cap->sublinkSpeedAttributes[i].exponent = (attr >> 4) & 0x3 ; + _ssplus_cap->sublinkSpeedAttributes[i].type = attr & 0x40 ? LIBUSB_SSPLUS_ATTR_TYPE_ASYM : LIBUSB_SSPLUS_ATTR_TYPE_SYM; + _ssplus_cap->sublinkSpeedAttributes[i].direction = attr & 0x80 ? LIBUSB_SSPLUS_ATTR_DIR_TX : LIBUSB_SSPLUS_ATTR_DIR_RX; + _ssplus_cap->sublinkSpeedAttributes[i].protocol = attr & 0x4000 ? LIBUSB_SSPLUS_ATTR_PROT_SSPLUS: LIBUSB_SSPLUS_ATTR_PROT_SS; + } + + *ssplus_usb_device_cap = _ssplus_cap; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a SuperSpeedPlus USB Device Capability descriptor obtained from + * libusb_get_ssplus_usb_device_capability_descriptor(). + * It is safe to call this function with a NULL ssplus_usb_device_cap + * parameter, in which case the function simply returns. + * + * Since version 1.0.28, \ref LIBUSB_API_VERSION >= 0x0100010B + * + * \param ssplus_usb_device_cap the SuperSpeedPlus USB Device Capability descriptor + * to free + */ +void API_EXPORTED libusb_free_ssplus_usb_device_capability_descriptor( + struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap) +{ + free(ssplus_usb_device_cap); +} + + + +/** \ingroup libusb_desc + * Free a SuperSpeed USB Device Capability descriptor obtained from + * libusb_get_ss_usb_device_capability_descriptor(). + * It is safe to call this function with a NULL ss_usb_device_cap + * parameter, in which case the function simply returns. + * + * \param ss_usb_device_cap the SuperSpeed USB Device Capability descriptor + * to free + */ +void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap) +{ + free(ss_usb_device_cap); +} + +/** \ingroup libusb_desc + * Get a Container ID descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_bos_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID + * \param container_id output location for the Container ID descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_container_id_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_container_id_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id) +{ + struct libusb_container_id_descriptor *_container_id; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_CONTAINER_ID); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE); + return LIBUSB_ERROR_IO; + } + + _container_id = malloc(sizeof(*_container_id)); + if (!_container_id) + return LIBUSB_ERROR_NO_MEM; + + _container_id->bLength = dev_cap->bLength; + _container_id->bDescriptorType = dev_cap->bDescriptorType; + _container_id->bDevCapabilityType = dev_cap->bDevCapabilityType; + _container_id->bReserved = dev_cap->dev_capability_data[0]; + memcpy(_container_id->ContainerID, &dev_cap->dev_capability_data[1], 16); + + *container_id = _container_id; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a Container ID descriptor obtained from + * libusb_get_container_id_descriptor(). + * It is safe to call this function with a NULL container_id parameter, + * in which case the function simply returns. + * + * \param container_id the Container ID descriptor to free + */ +void API_EXPORTED libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id) +{ + free(container_id); +} + +/** \ingroup libusb_desc + * Get a platform descriptor + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_bos_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_PLATFORM_DESCRIPTOR + * \param platform_descriptor output location for the Platform descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_platform_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor) +{ + struct libusb_platform_descriptor *_platform_descriptor; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_PLATFORM_DESCRIPTOR) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_PLATFORM_DESCRIPTOR); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE); + return LIBUSB_ERROR_IO; + } + + _platform_descriptor = malloc(dev_cap->bLength); + if (!_platform_descriptor) + return LIBUSB_ERROR_NO_MEM; + + _platform_descriptor->bLength = dev_cap->bLength; + _platform_descriptor->bDescriptorType = dev_cap->bDescriptorType; + _platform_descriptor->bDevCapabilityType = dev_cap->bDevCapabilityType; + _platform_descriptor->bReserved = dev_cap->dev_capability_data[0]; + memcpy(_platform_descriptor->PlatformCapabilityUUID, &(dev_cap->dev_capability_data[1]), 16); + + /* Capability data is located after reserved byte and 16 byte UUID */ + uint8_t* capability_data = dev_cap->dev_capability_data + 1 + 16; + + /* Capability data length is total descriptor length minus initial fields */ + size_t capability_data_length = dev_cap->bLength - (3 + 1 + 16); + + memcpy(_platform_descriptor->CapabilityData, capability_data, capability_data_length); + + *platform_descriptor = _platform_descriptor; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a platform descriptor obtained from + * libusb_get_platform_descriptor(). + * It is safe to call this function with a NULL platform_descriptor parameter, + * in which case the function simply returns. + * + * \param platform_descriptor the Platform descriptor to free + */ +void API_EXPORTED libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor) +{ + free(platform_descriptor); +} + +/** \ingroup libusb_desc + * Retrieve a string descriptor in C style ASCII. + * + * Wrapper around libusb_get_string_descriptor(). Uses the first language + * supported by the device. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for ASCII string descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length) +{ + union usbi_string_desc_buf str; + int r; + uint16_t langid, wdata; + + /* Asking for the zero'th index is special - it returns a string + * descriptor that contains all the language IDs supported by the + * device. Typically there aren't many - often only one. Language + * IDs are 16 bit numbers, and they start at the third byte in the + * descriptor. There's also no point in trying to read descriptor 0 + * with this function. See USB 2.0 specification section 9.6.7 for + * more information. + */ + + if (desc_index == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_get_string_descriptor(dev_handle, 0, 0, str.buf, 4); + if (r < 0) + return r; + else if (r != 4 || str.desc.bLength < 4 || str.desc.bDescriptorType != LIBUSB_DT_STRING) { + usbi_warn(HANDLE_CTX(dev_handle), "invalid language ID string descriptor"); + return LIBUSB_ERROR_IO; + } else if (str.desc.bLength & 1) + 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)); + if (r < 0) + return r; + else if (r < DESC_HEADER_LENGTH || str.desc.bLength > r || 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 (read %d)", str.desc.bLength, r); + + /* Stop one byte before the end to leave room for null termination. */ + int dest_max = length - 1; + + /* The descriptor has this number of wide characters */ + int src_max = (str.desc.bLength - 1 - 1) / 2; + + /* Neither read nor write more than the smallest buffer */ + int idx_max = MIN(dest_max, src_max); + + int idx; + for (idx = 0; idx < idx_max; ++idx) { + wdata = libusb_le16_to_cpu(str.desc.wData[idx]); + if (wdata < 0x80) + data[idx] = (unsigned char)wdata; + else + data[idx] = '?'; /* non-ASCII */ + } + + data[idx] = 0; /* null-terminate string */ + return idx; +} + +static int parse_iad_array(struct libusb_context *ctx, + struct libusb_interface_association_descriptor_array *iad_array, + const uint8_t *buffer, int size) +{ + uint8_t i; + struct usbi_descriptor_header header; + int consumed = 0; + const uint8_t *buf = buffer; + struct libusb_interface_association_descriptor *iad; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + /* First pass: Iterate through desc list, count number of IADs */ + iad_array->length = 0; + while (size - consumed >= DESC_HEADER_LENGTH) { + header.bLength = buf[0]; + header.bDescriptorType = buf[1]; + if (header.bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid descriptor bLength %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + else if (header.bLength > size - consumed) { + usbi_warn(ctx, "short config descriptor read %d/%u", + size - consumed, header.bLength); + return LIBUSB_ERROR_IO; + } + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION) + iad_array->length++; + buf += header.bLength; + consumed += header.bLength; + } + + iad_array->iad = NULL; + if (iad_array->length > 0) { + iad = calloc((size_t)iad_array->length, sizeof(*iad)); + if (!iad) + return LIBUSB_ERROR_NO_MEM; + + iad_array->iad = iad; + + /* Second pass: Iterate through desc list, fill IAD structures */ + int remaining = size; + i = 0; + do { + header.bLength = buffer[0]; + header.bDescriptorType = buffer[1]; + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION && (remaining >= LIBUSB_DT_INTERFACE_ASSOCIATION_SIZE)) { + iad[i].bLength = buffer[0]; + iad[i].bDescriptorType = buffer[1]; + iad[i].bFirstInterface = buffer[2]; + iad[i].bInterfaceCount = buffer[3]; + iad[i].bFunctionClass = buffer[4]; + iad[i].bFunctionSubClass = buffer[5]; + iad[i].bFunctionProtocol = buffer[6]; + iad[i].iFunction = buffer[7]; + i++; + } + + remaining -= header.bLength; + if (remaining < DESC_HEADER_LENGTH) { + break; + } + buffer += header.bLength; + } while (1); + } + + return LIBUSB_SUCCESS; +} + +static int raw_desc_to_iad_array(struct libusb_context *ctx, const uint8_t *buf, + int size, struct libusb_interface_association_descriptor_array **iad_array) +{ + struct libusb_interface_association_descriptor_array *_iad_array + = calloc(1, sizeof(*_iad_array)); + int r; + + if (!_iad_array) + return LIBUSB_ERROR_NO_MEM; + + r = parse_iad_array(ctx, _iad_array, buf, size); + if (r < 0) { + usbi_err(ctx, "parse_iad_array failed with error %d", r); + free(_iad_array); + return r; + } + + *iad_array = _iad_array; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for a given + * configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config_index the index of the configuration you wish to retrieve the + * IADs for. + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_interface_association_descriptors() + */ +int API_EXPORTED libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_dbg(DEVICE_CTX(dev), "IADs for config index %u", config_index); + if (config_index >= dev->device_descriptor.bNumConfigurations) + return LIBUSB_ERROR_NOT_FOUND; + + r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_config_descriptor(dev, config_index, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for the currently + * active configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_interface_association_descriptors + */ +int API_EXPORTED libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_active_config_descriptor(dev, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Free an array of interface association descriptors (IADs) obtained from + * libusb_get_interface_association_descriptors() or + * libusb_get_active_interface_association_descriptors(). + * It is safe to call this function with a NULL iad_array parameter, in which + * case the function simply returns. + * + * \param iad_array the IAD array to free + */ +void API_EXPORTED libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array) +{ + if (!iad_array) + return; + + if (iad_array->iad) + free((void*)iad_array->iad); + free(iad_array); +} + +/* + * \brief Copy a UTF-8 string with proper truncation if needed. + * + * \param tgt The target utf-8 string. If NULL, then tgt is ignored, + * tgt_size is forced to 0, and this function returns the + * required tgt_size for a subsequent call to this function. + * \param src The source utf-8 string. If NULL, then + * the source string is empty, the return length is 1, + * and, if tgt is not NULL, this function + * sets tgt[0] to the null terminator. + * \param tgt_size The size of target in bytes. + * \return the length of src in bytes, including the null terminator. + * + * utf8_copy(NULL, src, 0) is equivalent to strlen(src) + 1. + */ +static int usbi_utf8_copy(char *tgt, char const *src, int tgt_size) { + uint8_t* t = (uint8_t*)tgt; + uint8_t const* s = (uint8_t const*)src; + + if (NULL == src) { + if ((NULL != tgt) && (tgt_size > 0)) { + tgt[0] = 0; + } + return 1; + } + + if ((NULL == tgt) || (tgt_size <= 0)) { + return (int)(strlen(src) + 1); + } + + // copy UTF-8 string and compute length + int k = 0; + for (k = 0; s[k]; ++k) { + if (k < tgt_size) { + t[k] = s[k]; + } else { + break; + } + } + + if (k >= tgt_size) { + // truncate respecting UTF-8 character boundaries + int idx = tgt_size - 1; + while (idx && (0x80 == (t[idx] & 0xC0))) { // utf-8 continuation byte + --idx; + } + t[idx] = 0; + return (int)(strlen(src) + 1); + } else { + t[k++] = 0; + return k; + } +} + +/** \ingroup libusb_desc + * Retrieve a device string without needing to open the device. + * + * Since version v1.0.30 \ref LIBUSB_API_VERSION >= 0x0100010C + * + * \param dev the target device + * \param string_type the string type to retrieve + * \param data the data buffer for the UTF-8 encoded string. + * \param length the size of the data buffer in bytes. + * USB string descriptors cannot be longer than + * LIBUSB_DEVICE_STRING_BYTES_MAX. + * \returns a negative error code or + * the actual string length in bytes including the null terminator. + * \see libusb_get_string_descriptor() + * \see libusb_get_string_descriptor_ascii() + * + * This function works when the device is still closed since it relies + * on the operating system to provide the string. The operating system + * normally reads and caches the common string descriptors during + * USB enumeration. + * + * Since the USB string descriptor could be processed by the OS, + * this function returns a UTF-8 encoded string. + * + * The string will be returned untranslated or in the default OS language + * when supported by the OS and USB device. + * + * One way to call this function is using a buffer on the stack: + * + * char buffer[LIBUSB_DEVICE_STRING_BYTES_MAX]; + * int ret = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_SERIAL_NUMBER, buffer, sizeof(buffer)); + * if (ret < 0) { + * // handle error + * } + * + * This function is commonly used to get the serial number to allow + * for device selection before opening the selected device. + */ +int API_EXPORTED libusb_get_device_string(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length) { + char * s; + if (NULL == dev) { + return LIBUSB_ERROR_INVALID_PARAM; + } + if ((string_type < 0) || (string_type >= LIBUSB_DEVICE_STRING_COUNT)) { + return LIBUSB_ERROR_INVALID_PARAM; + } + if (length <= 0) { + return LIBUSB_ERROR_INVALID_PARAM; + } + if (NULL == data) { + length = 0; + data = NULL; + } else if (length > 0) { + *data = 0; // return an empty string on errors when possible + } + + if (NULL == dev->device_strings_utf8[string_type]) { + if (usbi_backend.get_device_string) { + s = malloc(LIBUSB_DEVICE_STRING_BYTES_MAX); + int rv = usbi_backend.get_device_string(dev, string_type, s, LIBUSB_DEVICE_STRING_BYTES_MAX); + if (rv < 0) { + free(s); + return rv; + } else { + dev->device_strings_utf8[string_type] = s; + } + } else { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + } + + s = dev->device_strings_utf8[string_type]; + if (NULL == s) { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return usbi_utf8_copy(data, s, length); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/hotplug.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/hotplug.c new file mode 100644 index 00000000..026e11d5 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/hotplug.c @@ -0,0 +1,489 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Hotplug functions for libusb + * Copyright © 2012-2021 Nathan Hjelm + * Copyright © 2012-2013 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +/** + * @defgroup libusb_hotplug Device hotplug event notification + * This page details how to use the libusb hotplug interface, where available. + * + * Be mindful that not all platforms currently implement hotplug notification and + * that you should first call on \ref libusb_has_capability() with parameter + * \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available. + * + * \page libusb_hotplug Device hotplug event notification + * + * \section hotplug_intro Introduction + * + * Version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, has added support + * for hotplug events on some platforms (you should test if your platform + * supports hotplug notification by calling \ref libusb_has_capability() with + * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). + * + * This interface allows you to request notification for the arrival and departure + * of matching USB devices. + * + * To receive hotplug notification you register a callback by calling + * \ref libusb_hotplug_register_callback(). This function will optionally return + * a callback handle that can be passed to \ref libusb_hotplug_deregister_callback(). + * + * A callback function must return an int (0 or 1) indicating whether the callback is + * expecting additional events. Returning 0 will rearm the callback and 1 will cause + * the callback to be deregistered. Note that when callbacks are called from + * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE + * flag, the callback return value is ignored. In other words, you cannot cause a + * callback to be deregistered by returning 1 when it is called from + * libusb_hotplug_register_callback(). + * + * Callbacks for a particular context are automatically deregistered by libusb_exit(). + * + * As of 1.0.16 there are two supported hotplug events: + * - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use + * - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available + * + * A hotplug event can listen for either or both of these events. + * + * Note: If you receive notification that a device has left and you have any + * 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. + * + * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered + * safe to call any libusb function that takes a libusb_device. It also safe to + * open a device and submit asynchronous transfers. However, most other functions + * that take a libusb_device_handle are not safe to call. Examples of such + * functions are any of the \ref libusb_syncio "synchronous API" functions or the blocking + * functions that retrieve various \ref libusb_desc "USB descriptors". These functions must + * be used outside of the context of the hotplug callback. + * + * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function + * is libusb_get_device_descriptor(). + * + * The following code provides an example of the usage of the hotplug interface: +\code +#include +#include +#include +#include + +static int count = 0; + +int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event, void *user_data) { + static libusb_device_handle *dev_handle = NULL; + struct libusb_device_descriptor desc; + int rc; + + (void)libusb_get_device_descriptor(dev, &desc); + + if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { + rc = libusb_open(dev, &dev_handle); + if (LIBUSB_SUCCESS != rc) { + printf("Could not open USB device\n"); + } + } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { + if (dev_handle) { + libusb_close(dev_handle); + dev_handle = NULL; + } + } else { + printf("Unhandled event %d\n", event); + } + count++; + + return 0; +} + +int main (void) { + libusb_hotplug_callback_handle callback_handle; + int rc; + + libusb_init_context(NULL, NULL, 0); + + rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, + LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, + &callback_handle); + if (LIBUSB_SUCCESS != rc) { + printf("Error creating a hotplug callback\n"); + libusb_exit(NULL); + return EXIT_FAILURE; + } + + while (count < 2) { + libusb_handle_events_completed(NULL, NULL); + nanosleep(&(struct timespec){0, 10000000UL}, NULL); + } + + libusb_hotplug_deregister_callback(NULL, callback_handle); + libusb_exit(NULL); + + return 0; +} +\endcode + */ + +#define VALID_HOTPLUG_EVENTS \ + (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + +#define VALID_HOTPLUG_FLAGS \ + (LIBUSB_HOTPLUG_ENUMERATE) + +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); +} + +static void usbi_recursively_remove_parents(struct libusb_device *dev, struct libusb_device *next_dev) +{ + if (dev && dev->parent_dev) { + if (usbi_atomic_load(&dev->parent_dev->refcnt) == 1) { + /* The parent was processed before this device in the list and + * therefore has its ref count already decremented for its own ref. + * The only remaining counted ref comes from its remaining single child. + * It will thus be released when its child will be released. So we + * can remove it from the list. This is safe as parent_dev cannot be + * equal to next_dev given that we know at this point that it was + * previously seen in the list. */ + assert (dev->parent_dev != next_dev); + if (dev->parent_dev->list.next && dev->parent_dev->list.prev) { + list_del(&dev->parent_dev->list); + } + } + + usbi_recursively_remove_parents(dev->parent_dev, next_dev); + } +} + +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); + } + + usbi_mutex_lock(&ctx->usb_devs_lock); /* hotplug thread might still be processing an already triggered event, possibly accessing this list as well */ + /* free all discovered devices */ + 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); + } + + usbi_recursively_remove_parents(dev, next_dev); + + libusb_unref_device(dev); + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + 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; + } + + if ((hotplug_cb->flags & USBI_HOTPLUG_VENDOR_ID_VALID) && + hotplug_cb->vendor_id != dev->device_descriptor.idVendor) { + return 0; + } + + if ((hotplug_cb->flags & USBI_HOTPLUG_PRODUCT_ID_VALID) && + hotplug_cb->product_id != dev->device_descriptor.idProduct) { + return 0; + } + + if ((hotplug_cb->flags & USBI_HOTPLUG_DEV_CLASS_VALID) && + hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) { + return 0; + } + + 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 usbi_hotplug_message *msg; + unsigned int event_flags; + + /* 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; + } + + 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(&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", + (void *) 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 usbi_hotplug_callback *hotplug_cb; + + /* check for sane values */ + 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)) || + (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || + !cb_fn) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + return LIBUSB_ERROR_NOT_SUPPORTED; + + ctx = usbi_get_context(ctx); + + hotplug_cb = calloc(1, sizeof(*hotplug_cb)); + if (!hotplug_cb) + return LIBUSB_ERROR_NO_MEM; + + hotplug_cb->flags = (uint8_t)events; + if (LIBUSB_HOTPLUG_MATCH_ANY != 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) { + hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID; + hotplug_cb->product_id = (uint16_t)product_id; + } + if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) { + hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID; + hotplug_cb->dev_class = (uint8_t)dev_class; + } + 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 */ + 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(&hotplug_cb->list, &ctx->hotplug_cbs); + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + usbi_dbg(ctx, "new hotplug cb %p with handle %d", + (void *) hotplug_cb, hotplug_cb->handle); + + if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) { + ssize_t i, len; + struct libusb_device **devs; + + len = libusb_get_device_list(ctx, &devs); + if (len < 0) { + libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle); + return (int)len; + } + + for (i = 0; i < len; i++) { + usbi_hotplug_match_cb(devs[i], + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, + hotplug_cb); + } + + libusb_free_device_list(devs, 1); + } + + if (callback_handle) + *callback_handle = hotplug_cb->handle; + + return LIBUSB_SUCCESS; +} + +void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle) +{ + struct usbi_hotplug_callback *hotplug_cb; + int deregistered = 0; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + return; + + 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 */ + hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE; + deregistered = 1; + break; + } + } + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + if (deregistered) { + unsigned int event_flags; + + usbi_mutex_lock(&ctx->event_data_lock); + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED; + if (!event_flags) + usbi_signal_event(&ctx->event); + usbi_mutex_unlock(&ctx->event_data_lock); + } +} + +DEFAULT_VISIBILITY +void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle) +{ + struct usbi_hotplug_callback *hotplug_cb; + void *user_data = NULL; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + return NULL; + + usbi_dbg(ctx, "get hotplug cb %d user data", 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) { + user_data = hotplug_cb->user_data; + break; + } + } + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + return user_data; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/io.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/io.c new file mode 100644 index 00000000..3dfc6c64 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/io.c @@ -0,0 +1,2869 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * I/O functions for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2019-2022 Nathan Hjelm + * Copyright © 2019-2022 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +/** + * \page libusb_io Synchronous and asynchronous device I/O + * + * \section io_intro Introduction + * + * If you're using libusb in your application, you're probably wanting to + * perform I/O with devices - you want to perform USB data transfers. + * + * libusb offers two separate interfaces for device I/O. This page aims to + * introduce the two in order to help you decide which one is more suitable + * for your application. You can also choose to use both interfaces in your + * application by considering each transfer on a case-by-case basis. + * + * Once you have read through the following discussion, you should consult the + * detailed API documentation pages for the details: + * - \ref libusb_syncio + * - \ref libusb_asyncio + * + * \section theory Transfers at a logical level + * + * At a logical level, USB transfers typically happen in two parts. For + * example, when reading data from a endpoint: + * -# A request for data is sent to the device + * -# Some time later, the incoming data is received by the host + * + * or when writing data to an endpoint: + * + * -# The data is sent to the device + * -# Some time later, the host receives acknowledgement from the device that + * the data has been transferred. + * + * There may be an indefinite delay between the two steps. Consider a + * fictional USB input device with a button that the user can press. In order + * to determine when the button is pressed, you would likely submit a request + * to read data on a bulk or interrupt endpoint and wait for data to arrive. + * Data will arrive when the button is pressed by the user, which is + * potentially hours later. + * + * libusb offers both a synchronous and an asynchronous interface to performing + * USB transfers. The main difference is that the synchronous interface + * combines both steps indicated above into a single function call, whereas + * the asynchronous interface separates them. + * + * \section sync The synchronous interface + * + * The synchronous I/O interface allows you to perform a USB transfer with + * a single function call. When the function call returns, the transfer has + * completed and you can parse the results. + * + * 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 + * in the following style: +\code +unsigned char data[4]; +int actual_length; +int r = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_IN, data, sizeof(data), &actual_length, 0); +if (r == 0 && actual_length == sizeof(data)) { + // results of the transaction can now be found in the data buffer + // parse them here and report button press +} else { + error(); +} +\endcode + * + * The main advantage of this model is simplicity: you did everything with + * a single simple function call. + * + * However, this interface has its limitations. Your application will sleep + * inside libusb_bulk_transfer() until the transaction has completed. If it + * takes the user 3 hours to press the button, your application will be + * sleeping for that long. Execution will be tied up inside the library - + * the entire thread will be useless for that duration. + * + * Another issue is that by tying up the thread with that single transaction + * there is no possibility of performing I/O with multiple endpoints and/or + * multiple devices simultaneously, unless you resort to creating one thread + * per transaction. + * + * Additionally, there is no opportunity to cancel the transfer after the + * request has been submitted. + * + * For details on how to use the synchronous API, see the + * \ref libusb_syncio "synchronous I/O API documentation" pages. + * + * \section async The asynchronous interface + * + * Asynchronous I/O is the most significant new feature in libusb-1.0. + * Although it is a more complex interface, it solves all the issues detailed + * above. + * + * Instead of providing which functions that block until the I/O has complete, + * libusb's asynchronous interface presents non-blocking functions which + * begin a transfer and then return immediately. Your application passes a + * callback function pointer to this non-blocking function, which libusb will + * call with the results of the transaction when it has completed. + * + * Transfers which have been submitted through the non-blocking functions + * can be cancelled with a separate function call. + * + * The non-blocking nature of this interface allows you to be simultaneously + * performing I/O to multiple endpoints on multiple devices, without having + * to use threads. + * + * This added flexibility does come with some complications though: + * - In the interest of being a lightweight library, libusb does not create + * threads and can only operate when your application is calling into it. Your + * application must call into libusb from it's main loop when events are ready + * to be handled, or you must use some other scheme to allow libusb to + * undertake whatever work needs to be done. + * - libusb also needs to be called into at certain fixed points in time in + * order to accurately handle transfer timeouts. + * - Memory handling becomes more complex. You cannot use stack memory unless + * the function with that stack is guaranteed not to return until the transfer + * callback has finished executing. + * - You generally lose some linearity from your code flow because submitting + * the transfer request is done in a separate function from where the transfer + * results are handled. This becomes particularly obvious when you want to + * submit a second transfer based on the results of an earlier transfer. + * + * Internally, libusb's synchronous interface is expressed in terms of function + * calls to the asynchronous interface. + * + * For details on how to use the asynchronous API, see the + * \ref libusb_asyncio "asynchronous I/O API" documentation pages. + */ + + +/** + * \page libusb_packetoverflow Packets and overflows + * + * \section packets Packet abstraction + * + * The USB specifications describe how data is transmitted in packets, with + * constraints on packet size defined by endpoint descriptors. The host must + * not send data payloads larger than the endpoint's maximum packet size. + * + * libusb and the underlying OS abstract out the packet concept, allowing you + * to request transfers of any size. Internally, the request will be divided + * up into correctly-sized packets. You do not have to be concerned with + * packet sizes, but there is one exception when considering overflows. + * + * \section overflow Bulk/interrupt transfer overflows + * + * When requesting data on a bulk endpoint, libusb requires you to supply a + * buffer and the maximum number of bytes of data that libusb can put in that + * buffer. However, the size of the buffer is not communicated to the device - + * the device is just asked to send any amount of data. + * + * There is no problem if the device sends an amount of data that is less than + * or equal to the buffer size. libusb reports this condition to you through + * the \ref libusb_transfer::actual_length "libusb_transfer.actual_length" + * field. + * + * Problems may occur if the device attempts to send more data than can fit in + * the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but + * other behaviour is largely undefined: actual_length may or may not be + * accurate, the chunk of data that can fit in the buffer (before overflow) + * may or may not have been transferred. + * + * Overflows are nasty, but can be avoided. Even though you were told to + * ignore packets above, think about the lower level details: each transfer is + * split into packets (typically small, with a maximum size of 512 bytes). + * Overflows can only happen if the final packet in an incoming data transfer + * is smaller than the actual packet that the device wants to transfer. + * Therefore, you will never see an overflow if your transfer buffer size is a + * multiple of the endpoint's packet size: the final packet will either + * fill up completely or will be only partially filled. + */ + +/** + * @defgroup libusb_asyncio Asynchronous device I/O + * + * This page details libusb's asynchronous (non-blocking) API for USB device + * I/O. This interface is very powerful but is also quite complex - you will + * need to read this page carefully to understand the necessary considerations + * and issues surrounding use of this interface. Simplistic applications + * may wish to consider the \ref libusb_syncio "synchronous I/O API" instead. + * + * The asynchronous interface is built around the idea of separating transfer + * submission and handling of transfer completion (the synchronous model + * combines both of these into one). There may be a long delay between + * submission and completion, however the asynchronous submission function + * is non-blocking so will return control to your application during that + * potentially long delay. + * + * \section asyncabstraction Transfer abstraction + * + * For the asynchronous I/O, libusb implements the concept of a generic + * transfer entity for all types of I/O (control, bulk, interrupt, + * isochronous). The generic transfer object must be treated slightly + * differently depending on which type of I/O you are performing with it. + * + * This is represented by the public libusb_transfer structure type. + * + * \section asynctrf Asynchronous transfers + * + * We can view asynchronous I/O as a 5 step process: + * -# Allocation: allocate a libusb_transfer + * -# Filling: populate the libusb_transfer instance with information + * about the transfer you wish to perform + * -# Submission: ask libusb to submit the transfer + * -# Completion handling: examine transfer results in the + * libusb_transfer structure + * -# Deallocation: clean up resources + * + * + * \subsection asyncalloc Allocation + * + * This step involves allocating memory for a USB transfer. This is the + * generic transfer object mentioned above. At this stage, the transfer + * is "blank" with no details about what type of I/O it will be used for. + * + * Allocation is done with the libusb_alloc_transfer() function. You must use + * this function rather than allocating your own transfers. + * + * \subsection asyncfill Filling + * + * This step is where you take a previously allocated transfer and fill it + * with information to determine the message type and direction, data buffer, + * callback function, etc. + * + * You can either fill the required fields yourself or you can use the + * helper functions: libusb_fill_control_transfer(), libusb_fill_bulk_transfer() + * and libusb_fill_interrupt_transfer(). + * + * \subsection asyncsubmit Submission + * + * When you have allocated a transfer and filled it, you can submit it using + * libusb_submit_transfer(). This function returns immediately but can be + * regarded as firing off the I/O request in the background. + * + * \subsection asynccomplete Completion handling + * + * After a transfer has been submitted, one of four things can happen to it: + * + * - The transfer completes (i.e. some data was transferred) + * - The transfer has a timeout and the timeout expires before all data is + * transferred + * - The transfer fails due to an error + * - The transfer is cancelled + * + * Each of these will cause the user-specified transfer callback function to + * be invoked. It is up to the callback function to determine which of the + * above actually happened and to act accordingly. + * + * The user-specified callback is passed a pointer to the libusb_transfer + * structure which was used to setup and submit the transfer. At completion + * time, libusb has populated this structure with results of the transfer: + * success or failure reason, number of bytes of data transferred, etc. See + * the libusb_transfer structure documentation for more information. + * + * Important Note: The user-specified callback is called from an event + * handling context. It is therefore important that no calls are made into + * libusb that will attempt to perform any event handling. Examples of such + * functions are any listed in the \ref libusb_syncio "synchronous API" and any of + * the blocking functions that retrieve \ref libusb_desc "USB descriptors". + * + * \subsection Deallocation + * + * When a transfer has completed (i.e. the callback function has been invoked), + * you are advised to free the transfer (unless you wish to resubmit it, see + * below). Transfers are deallocated with libusb_free_transfer(). + * + * It is undefined behaviour to free a transfer which has not completed. + * + * \section asyncresubmit Resubmission + * + * You may be wondering why allocation, filling, and submission are all + * separated above where they could reasonably be combined into a single + * operation. + * + * The reason for separation is to allow you to resubmit transfers without + * having to allocate new ones every time. This is especially useful for + * common situations dealing with interrupt endpoints - you allocate one + * transfer, fill and submit it, and when it returns with results you just + * resubmit it for the next interrupt. + * + * \section asynccancel Cancellation + * + * Another advantage of using the asynchronous interface is that you have + * the ability to cancel transfers which have not yet completed. This is + * done by calling the libusb_cancel_transfer() function. + * + * libusb_cancel_transfer() is asynchronous/non-blocking in itself. When the + * cancellation actually completes, the transfer's callback function will + * be invoked, and the callback function should check the transfer status to + * determine that it was cancelled. + * + * On macOS and iOS it is not possible to cancel a single transfer. In this + * case cancelling one transfer on an endpoint will cause all transfers on + * that endpoint to be cancelled. + * + * Freeing the transfer after it has been cancelled but before cancellation + * has completed will result in undefined behaviour. + * + * \attention + * When a transfer is cancelled, some of the data may have been transferred. + * libusb will communicate this to you in the transfer callback. + * Do not assume that no data was transferred. + * + * \section asyncpartial Partial data transfer resulting from cancellation + * + * As noted above, some of the data may have been transferred at the time a + * transfer is cancelled. It is helpful to see how this is possible if you + * consider a bulk transfer to an endpoint with a packet size of 64 bytes. + * Supposing you submit a 512-byte transfer to this endpoint, the operating + * system will divide this transfer up into 8 separate 64-byte frames that the + * host controller will schedule for the device to transfer data. If this + * transfer is cancelled while the device is transferring data, a subset of + * these frames may be descheduled from the host controller before the device + * has the opportunity to finish transferring data to the host. + * + * What your application should do with a partial data transfer is a policy + * decision; there is no single answer that satisfies the needs of every + * application. The data that was successfully transferred should be + * considered entirely valid, but your application must decide what to do with + * the remaining data that was not transferred. Some possible actions to take + * are: + * - Resubmit another transfer for the remaining data, possibly with a shorter + * timeout + * - Discard the partially transferred data and report an error + * + * \section asynctimeout Timeouts + * + * When a transfer times out, libusb internally notes this and attempts to + * cancel the transfer. As noted in \ref asyncpartial "above", it is possible + * that some of the data may actually have been transferred. Your application + * should always check how much data was actually transferred once the + * transfer completes and act accordingly. + * + * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints + * + * If your device does not have predictable transfer sizes (or it misbehaves), + * your application may submit a request for data on an IN endpoint which is + * smaller than the data that the device wishes to send. In some circumstances + * this will cause an overflow, which is a nasty condition to deal with. See + * the \ref libusb_packetoverflow page for discussion. + * + * \section asyncctrl Considerations for control transfers + * + * The libusb_transfer structure is generic and hence does not + * include specific fields for the control-specific setup packet structure. + * + * In order to perform a control transfer, you must place the 8-byte setup + * packet at the start of the data buffer. To simplify this, you could + * cast the buffer pointer to type struct libusb_control_setup, or you can + * use the helper function libusb_fill_control_setup(). + * + * The wLength field placed in the setup packet must be the length you would + * expect to be sent in the setup packet: the length of the payload that + * follows (or the expected maximum number of bytes to receive). However, + * the length field of the libusb_transfer object must be the length of + * the data buffer - i.e. it should be wLength plus the size of + * the setup packet (LIBUSB_CONTROL_SETUP_SIZE). + * + * If you use the helper functions, this is simplified for you: + * -# Allocate a buffer of size LIBUSB_CONTROL_SETUP_SIZE plus the size of the + * data you are sending/requesting. + * -# Call libusb_fill_control_setup() on the data buffer, using the transfer + * request size as the wLength value (i.e. do not include the extra space you + * allocated for the control setup). + * -# If this is a host-to-device transfer, place the data to be transferred + * in the data buffer, starting at offset LIBUSB_CONTROL_SETUP_SIZE. + * -# Call libusb_fill_control_transfer() to associate the data buffer with + * the transfer (and to set the remaining details such as callback and timeout). + * - Note that there is no parameter to set the length field of the transfer. + * The length is automatically inferred from the wLength field of the setup + * packet. + * -# Submit the transfer. + * + * The multi-byte control setup fields (wValue, wIndex and wLength) must + * be given in little-endian byte order (the endianness of the USB bus). + * Endianness conversion is transparently handled by + * libusb_fill_control_setup() which is documented to accept host-endian + * values. + * + * Further considerations are needed when handling transfer completion in + * your callback function: + * - As you might expect, the setup packet will still be sitting at the start + * of the data buffer. + * - If this was a device-to-host transfer, the received data will be sitting + * at offset LIBUSB_CONTROL_SETUP_SIZE into the buffer. + * - The actual_length field of the transfer structure is relative to the + * wLength of the setup packet, rather than the size of the data buffer. So, + * if your wLength was 4, your transfer's length was 12, then you + * should expect an actual_length of 4 to indicate that the data was + * transferred in entirety. + * + * To simplify parsing of setup packets and obtaining the data from the + * correct offset, you may wish to use the libusb_control_transfer_get_data() + * and libusb_control_transfer_get_setup() functions within your transfer + * callback. + * + * Even though control endpoints do not halt, a completed control transfer + * may have a LIBUSB_TRANSFER_STALL status code. This indicates the control + * request was not supported. + * + * \section asyncintr Considerations for interrupt transfers + * + * All interrupt transfers are performed using the polling interval presented + * by the bInterval value of the endpoint descriptor. + * + * \section asynciso Considerations for isochronous transfers + * + * Isochronous transfers are more complicated than transfers to + * non-isochronous endpoints. + * + * To perform I/O to an isochronous endpoint, allocate the transfer by calling + * libusb_alloc_transfer() with an appropriate number of isochronous packets. + * + * During filling, set \ref libusb_transfer::type "type" to + * \ref libusb_transfer_type::LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + * "LIBUSB_TRANSFER_TYPE_ISOCHRONOUS", and set + * \ref libusb_transfer::num_iso_packets "num_iso_packets" to a value less than + * or equal to the number of packets you requested during allocation. + * libusb_alloc_transfer() does not set either of these fields for you, given + * that you might not even use the transfer on an isochronous endpoint. + * + * Next, populate the length field for the first num_iso_packets entries in + * the \ref libusb_transfer::iso_packet_desc "iso_packet_desc" array. Section + * 5.6.3 of the USB2 specifications describe how the maximum isochronous + * packet length is determined by the wMaxPacketSize field in the endpoint + * descriptor. + * Two functions can help you here: + * + * - libusb_get_max_iso_packet_size() is an easy way to determine the max + * packet size for an isochronous endpoint. Note that the maximum packet + * size is actually the maximum number of bytes that can be transmitted in + * a single microframe, therefore this function multiplies the maximum number + * of bytes per transaction by the number of transaction opportunities per + * microframe. + * - libusb_set_iso_packet_lengths() assigns the same length to all packets + * within a transfer, which is usually what you want. + * + * For outgoing transfers, you'll obviously fill the buffer and populate the + * packet descriptors in hope that all the data gets transferred. For incoming + * transfers, you must ensure the buffer has sufficient capacity for + * the situation where all packets transfer the full amount of requested data. + * + * Completion handling requires some extra consideration. The + * \ref libusb_transfer::actual_length "actual_length" field of the transfer + * is meaningless and should not be examined; instead you must refer to the + * \ref libusb_iso_packet_descriptor::actual_length "actual_length" field of + * each individual packet. + * + * The \ref libusb_transfer::status "status" field of the transfer is also a + * little misleading: + * - If the packets were submitted and the isochronous data microframes + * completed normally, status will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_COMPLETED + * "LIBUSB_TRANSFER_COMPLETED". Note that bus errors and software-incurred + * delays are not counted as transfer errors; the transfer.status field may + * indicate COMPLETED even if some or all of the packets failed. Refer to + * the \ref libusb_iso_packet_descriptor::status "status" field of each + * individual packet to determine packet failures. + * - The status field will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR + * "LIBUSB_TRANSFER_ERROR" only when serious errors were encountered. + * - Other transfer status codes occur with normal behaviour. + * + * The data for each packet will be found at an offset into the buffer that + * can be calculated as if each prior packet completed in full. The + * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple() + * functions may help you here. + * + * \section asynclimits Transfer length limitations + * + * Some operating systems may impose limits on the length of the transfer data + * buffer or, in the case of isochronous transfers, the length of individual + * isochronous packets. Such limits can be difficult for libusb to detect, so + * in most cases the library will simply try and submit the transfer as set up + * by you. If the transfer fails to submit because it is too large, + * libusb_submit_transfer() will return + * \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM". + * + * The following are known limits for control transfer lengths. Note that this + * length includes the 8-byte setup packet. + * - Linux (4,096 bytes) + * - Windows (4,096 bytes) + * + * \section asyncmem Memory caveats + * + * In most circumstances, it is not safe to use stack memory for transfer + * buffers. This is because the function that fired off the asynchronous + * transfer may return before libusb has finished using the buffer, and when + * the function returns it's stack gets destroyed. This is true for both + * host-to-device and device-to-host transfers. + * + * The only case in which it is safe to use stack memory is where you can + * guarantee that the function owning the stack space for the buffer does not + * return until after the transfer's callback function has completed. In every + * other case, you need to use heap memory instead. + * + * \section asyncflags Fine control + * + * Through using this asynchronous interface, you may find yourself repeating + * a few simple operations many times. You can apply a bitwise OR of certain + * flags to a transfer to simplify certain things: + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_SHORT_NOT_OK + * "LIBUSB_TRANSFER_SHORT_NOT_OK" results in transfers which transferred + * less than the requested amount of data being marked with status + * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR "LIBUSB_TRANSFER_ERROR" + * (they would normally be regarded as COMPLETED) + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER + * "LIBUSB_TRANSFER_FREE_BUFFER" allows you to ask libusb to free the transfer + * buffer when freeing the transfer. + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_TRANSFER + * "LIBUSB_TRANSFER_FREE_TRANSFER" causes libusb to automatically free the + * transfer after the transfer callback returns. + * + * \section asyncevent Event handling + * + * An asynchronous model requires that libusb perform work at various + * points in time - namely processing the results of previously-submitted + * transfers and invoking the user-supplied callback function. + * + * This gives rise to the libusb_handle_events() function which your + * application must call into when libusb has work do to. This gives libusb + * the opportunity to reap pending transfers, invoke callbacks, etc. + * + * \note + * All event handling is performed by whichever thread calls the + * libusb_handle_events() function. libusb does not invoke any callbacks + * outside of this context. Consequently, any callbacks will be run on the + * thread that calls the libusb_handle_events() function. + * + * When to call the libusb_handle_events() function depends on which model + * your application decides to use. The 2 different approaches: + * + * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated + * thread. + * -# Integrate libusb with your application's main event loop. libusb + * exposes a set of file descriptors which allow you to do this. + * + * The first approach has the big advantage that it will also work on Windows + * were libusb' poll API for select / poll integration is not available. So + * if you want to support Windows and use the async API, you must use this + * approach, see the \ref eventthread "Using an event handling thread" section + * below for details. + * + * If you prefer a single threaded approach with a single central event loop, + * see the \ref libusb_poll "polling and timing" section for how to integrate libusb + * into your application's main event loop. + * + * \section eventthread Using an event handling thread + * + * Lets begin with stating the obvious: If you're going to use a separate + * thread for libusb event handling, your callback functions MUST be + * thread-safe. + * + * Other then that doing event handling from a separate thread, is mostly + * simple. You can use an event thread function as follows: +\code +void *event_thread_func(void *ctx) +{ + while (event_thread_run) + libusb_handle_events(ctx); + + return NULL; +} +\endcode + * + * There is one caveat though, stopping this thread requires setting the + * event_thread_run variable to 0, and after that libusb_handle_events() needs + * to return control to event_thread_func. But unless some event happens, + * libusb_handle_events() will not return. + * + * There are 2 different ways of dealing with this, depending on if your + * application uses libusb' \ref libusb_hotplug "hotplug" support or not. + * + * Applications which do not use hotplug support, should not start the event + * thread until after their first call to libusb_open(), and should stop the + * thread when closing the last open device as follows: +\code +void my_close_handle(libusb_device_handle *dev_handle) +{ + if (open_devs == 1) + event_thread_run = 0; + + libusb_close(dev_handle); // This wakes up libusb_handle_events() + + if (open_devs == 1) + pthread_join(event_thread); + + open_devs--; +} +\endcode + * + * Applications using hotplug support should start the thread at program init, + * after having successfully called libusb_hotplug_register_callback(), and + * should stop the thread at program exit as follows: +\code +void my_libusb_exit(void) +{ + event_thread_run = 0; + libusb_hotplug_deregister_callback(ctx, hotplug_cb_handle); // This wakes up libusb_handle_events() + pthread_join(event_thread); + libusb_exit(ctx); +} +\endcode + */ + +/** + * @defgroup libusb_poll Polling and timing + * + * This page documents libusb's functions for polling events and timing. + * These functions are only necessary for users of the + * \ref libusb_asyncio "asynchronous API". If you are only using the simpler + * \ref libusb_syncio "synchronous API" then you do not need to ever call these + * functions. + * + * The justification for the functionality described here has already been + * discussed in the \ref asyncevent "event handling" section of the + * asynchronous API documentation. In summary, libusb does not create internal + * threads for event processing and hence relies on your application calling + * into libusb at certain points in time so that pending events can be handled. + * + * Your main loop is probably already calling poll() or select() or a + * variant on a set of file descriptors for other event sources (e.g. keyboard + * button presses, mouse movements, network sockets, etc). You then add + * libusb's file descriptors to your poll()/select() calls, and when activity + * is detected on such descriptors you know it is time to call + * libusb_handle_events(). + * + * There is one final event handling complication. libusb supports + * asynchronous transfers which time out after a specified time period. + * + * On some platforms a timerfd is used, so the timeout handling is just another + * fd, on other platforms this requires that libusb is called into at or after + * the timeout to handle it. So, in addition to considering libusb's file + * descriptors in your main event loop, you must also consider that libusb + * sometimes needs to be called into at fixed points in time even when there + * is no file descriptor activity, see \ref polltime details. + * + * In order to know precisely when libusb needs to be called into, libusb + * offers you a set of pollable file descriptors and information about when + * the next timeout expires. + * + * If you are using the asynchronous I/O API, you must take one of the two + * following options, otherwise your I/O will not complete. + * + * \section pollsimple The simple option + * + * If your application revolves solely around libusb and does not need to + * handle other event sources, you can have a program structure as follows: +\code +// initialize libusb +// find and open device +// maybe fire off some initial async I/O + +while (user_has_not_requested_exit) + libusb_handle_events(ctx); + +// clean up and exit +\endcode + * + * With such a simple main loop, you do not have to worry about managing + * sets of file descriptors or handling timeouts. libusb_handle_events() will + * handle those details internally. + * + * \section libusb_pollmain The more advanced option + * + * \note This functionality is currently only available on Unix-like platforms. + * On Windows, libusb_get_pollfds() simply returns NULL. Applications which + * want to support Windows are advised to use an \ref eventthread + * "event handling thread" instead. + * + * In more advanced applications, you will already have a main loop which + * is monitoring other event sources: network sockets, X11 events, mouse + * movements, etc. Through exposing a set of file descriptors, libusb is + * designed to cleanly integrate into such main loops. + * + * In addition to polling file descriptors for the other event sources, you + * take a set of file descriptors from libusb and monitor those too. When you + * detect activity on libusb's file descriptors, you call + * libusb_handle_events_timeout() in non-blocking mode. + * + * What's more, libusb may also need to handle events at specific moments in + * time. No file descriptor activity is generated at these times, so your + * own application needs to be continually aware of when the next one of these + * moments occurs (through calling libusb_get_next_timeout()), and then it + * needs to call libusb_handle_events_timeout() in non-blocking mode when + * these moments occur. This means that you need to adjust your + * poll()/select() timeout accordingly. + * + * libusb provides you with a set of file descriptors to poll and expects you + * to poll all of them, treating them as a single entity. The meaning of each + * file descriptor in the set is an internal implementation detail, + * platform-dependent and may vary from release to release. Don't try and + * interpret the meaning of the file descriptors, just do as libusb indicates, + * polling all of them at once. + * + * In pseudo-code, you want something that looks like: +\code +// initialise libusb + +libusb_get_pollfds(ctx) +while (user has not requested application exit) { + libusb_get_next_timeout(ctx); + poll(on libusb file descriptors plus any other event sources of interest, + using a timeout no larger than the value libusb just suggested) + if (poll() indicated activity on libusb file descriptors) + libusb_handle_events_timeout(ctx, &zero_tv); + if (time has elapsed to or beyond the libusb timeout) + libusb_handle_events_timeout(ctx, &zero_tv); + // handle events from other sources here +} + +// clean up and exit +\endcode + * + * \subsection polltime Notes on time-based events + * + * The above complication with having to track time and call into libusb at + * specific moments is a bit of a headache. For maximum compatibility, you do + * need to write your main loop as above, but you may decide that you can + * restrict the supported platforms of your application and get away with + * a more simplistic scheme. + * + * These time-based event complications are \b not required on the following + * platforms: + * - Darwin + * - Linux, provided that the following version requirements are satisfied: + * - Linux v2.6.27 or newer, compiled with timerfd support + * - glibc v2.9 or newer + * - libusb v1.0.5 or newer + * + * Under these configurations, libusb_get_next_timeout() will \em always return + * 0, so your main loop can be simplified to: +\code +// initialise libusb + +libusb_get_pollfds(ctx) +while (user has not requested application exit) { + poll(on libusb file descriptors plus any other event sources of interest, + using any timeout that you like) + if (poll() indicated activity on libusb file descriptors) + libusb_handle_events_timeout(ctx, &zero_tv); + // handle events from other sources here +} + +// clean up and exit +\endcode + * + * Do remember that if you simplify your main loop to the above, you will + * lose compatibility with some platforms (including legacy Linux platforms, + * and any future platforms supported by libusb which may have time-based + * event requirements). The resultant problems will likely appear as + * strange bugs in your application. + * + * You can use the libusb_pollfds_handle_timeouts() function to do a runtime + * check to see if it is safe to ignore the time-based event complications. + * If your application has taken the shortcut of ignoring libusb's next timeout + * in your main loop, then you are advised to check the return value of + * libusb_pollfds_handle_timeouts() during application startup, and to abort + * if the platform does suffer from these timing complications. + * + * \subsection fdsetchange Changes in the file descriptor set + * + * The set of file descriptors that libusb uses as event sources may change + * during the life of your application. Rather than having to repeatedly + * call libusb_get_pollfds(), you can set up notification functions for when + * the file descriptor set changes using libusb_set_pollfd_notifiers(). + * + * \subsection mtissues Multi-threaded considerations + * + * Unfortunately, the situation is complicated further when multiple threads + * come into play. If two threads are monitoring the same file descriptors, + * the fact that only one thread will be woken up when an event occurs causes + * some headaches. + * + * The events lock, event waiters lock, and libusb_handle_events_locked() + * entities are added to solve these problems. You do not need to be concerned + * with these entities otherwise. + * + * See the extra documentation: \ref libusb_mtasync + */ + +/** \page libusb_mtasync Multi-threaded applications and asynchronous I/O + * + * libusb is a thread-safe library, but extra considerations must be applied + * to applications which interact with libusb from multiple threads. + * + * The underlying issue that must be addressed is that all libusb I/O + * revolves around monitoring file descriptors through the poll()/select() + * system calls. This is directly exposed at the + * \ref libusb_asyncio "asynchronous interface" but it is important to note that the + * \ref libusb_syncio "synchronous interface" is implemented on top of the + * asynchronous interface, therefore the same considerations apply. + * + * The issue is that if two or more threads are concurrently calling poll() + * or select() on libusb's file descriptors then only one of those threads + * will be woken up when an event arrives. The others will be completely + * oblivious that anything has happened. + * + * Consider the following pseudo-code, which submits an asynchronous transfer + * then waits for its completion. This style is one way you could implement a + * synchronous interface on top of the asynchronous interface (and libusb + * does something similar, albeit more advanced due to the complications + * explained on this page). + * +\code +void cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; +} + +void myfunc() { + struct libusb_transfer *transfer; + unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2))); + int completed = 0; + + transfer = libusb_alloc_transfer(0); + libusb_fill_control_setup(buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x04, 0x01, 0, 0); + libusb_fill_control_transfer(transfer, dev, buffer, cb, &completed, 1000); + libusb_submit_transfer(transfer); + + while (!completed) { + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_timeout(ctx, &zero_tv); + } + printf("completed!"); + // other code here +} +\endcode + * + * Here we are serializing completion of an asynchronous event + * against a condition - the condition being completion of a specific transfer. + * The poll() loop has a long timeout to minimize CPU usage during situations + * when nothing is happening (it could reasonably be unlimited). + * + * If this is the only thread that is polling libusb's file descriptors, there + * is no problem: there is no danger that another thread will swallow up the + * event that we are interested in. On the other hand, if there is another + * thread polling the same descriptors, there is a chance that it will receive + * the event that we were interested in. In this situation, myfunc() + * will only realise that the transfer has completed on the next iteration of + * the loop, up to 120 seconds later. Clearly a two-minute delay is + * undesirable, and don't even think about using short timeouts to circumvent + * this issue! + * + * The solution here is to ensure that no two threads are ever polling the + * file descriptors at the same time. A naive implementation of this would + * impact the capabilities of the library, so libusb offers the scheme + * documented below to ensure no loss of functionality. + * + * Before we go any further, it is worth mentioning that all libusb-wrapped + * event handling procedures fully adhere to the scheme documented below. + * This includes libusb_handle_events() and its variants, and all the + * synchronous I/O functions - libusb hides this headache from you. + * + * \section Using libusb_handle_events() from multiple threads + * + * Even when only using libusb_handle_events() and synchronous I/O functions, + * you can still have a race condition. You might be tempted to solve the + * above with libusb_handle_events() like so: + * +\code + libusb_submit_transfer(transfer); + + while (!completed) { + libusb_handle_events(ctx); + } + printf("completed!"); +\endcode + * + * This however has a race between the checking of completed and + * libusb_handle_events() acquiring the events lock, so another thread + * could have completed the transfer, resulting in this thread hanging + * until either a timeout or another event occurs. See also commit + * 6696512aade99bb15d6792af90ae329af270eba6 which fixes this in the + * synchronous API implementation of libusb. + * + * Fixing this race requires checking the variable completed only after + * taking the event lock, which defeats the concept of just calling + * libusb_handle_events() without worrying about locking. This is why + * libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed() + * and libusb_handle_events_completed() functions, which handles doing the + * completion check for you after they have acquired the lock: + * +\code + libusb_submit_transfer(transfer); + + while (!completed) { + libusb_handle_events_completed(ctx, &completed); + } + printf("completed!"); +\endcode + * + * This nicely fixes the race in our example. Note that if all you want to + * do is submit a single transfer and wait for its completion, then using + * one of the synchronous I/O functions is much easier. + * + * \note + * The `completed` variable must be modified while holding the event lock, + * otherwise a race condition can still exist. It is simplest to do so from + * within the transfer callback as shown above. + * + * \section eventlock The events lock + * + * The problem is when we consider the fact that libusb exposes file + * descriptors to allow for you to integrate asynchronous USB I/O into + * existing main loops, effectively allowing you to do some work behind + * libusb's back. If you do take libusb's file descriptors and pass them to + * poll()/select() yourself, you need to be aware of the associated issues. + * + * The first concept to be introduced is the events lock. The events lock + * is used to serialize threads that want to handle events, such that only + * one thread is handling events at any one time. + * + * You must take the events lock before polling libusb file descriptors, + * using libusb_lock_events(). You must release the lock as soon as you have + * aborted your poll()/select() loop, using libusb_unlock_events(). + * + * \section threadwait Letting other threads do the work for you + * + * Although the events lock is a critical part of the solution, it is not + * enough on it's own. You might wonder if the following is sufficient... +\code + libusb_lock_events(ctx); + while (!completed) { + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_timeout(ctx, &zero_tv); + } + libusb_unlock_events(ctx); +\endcode + * ...and the answer is that it is not. This is because the transfer in the + * code shown above may take a long time (say 30 seconds) to complete, and + * the lock is not released until the transfer is completed. + * + * Another thread with similar code that wants to do event handling may be + * working with a transfer that completes after a few milliseconds. Despite + * having such a quick completion time, the other thread cannot check that + * status of its transfer until the code above has finished (30 seconds later) + * due to contention on the lock. + * + * To solve this, libusb offers you a mechanism to determine when another + * thread is handling events. It also offers a mechanism to block your thread + * until the event handling thread has completed an event (and this mechanism + * does not involve polling of file descriptors). + * + * After determining that another thread is currently handling events, you + * obtain the event waiters lock using libusb_lock_event_waiters(). + * You then re-check that some other thread is still handling events, and if + * so, you call libusb_wait_for_event(). + * + * libusb_wait_for_event() puts your application to sleep until an event + * occurs, or until a thread releases the events lock. When either of these + * things happen, your thread is woken up, and should re-check the condition + * it was waiting on. It should also re-check that another thread is handling + * events, and if not, it should start handling events itself. + * + * This looks like the following, as pseudo-code: +\code +retry: +if (libusb_try_lock_events(ctx) == 0) { + // we obtained the event lock: do our own event handling + while (!completed) { + if (!libusb_event_handling_ok(ctx)) { + libusb_unlock_events(ctx); + goto retry; + } + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_locked(ctx, 0); + } + libusb_unlock_events(ctx); +} else { + // another thread is doing event handling. wait for it to signal us that + // an event has completed + libusb_lock_event_waiters(ctx); + + while (!completed) { + // now that we have the event waiters lock, double check that another + // thread is still handling events for us. (it may have ceased handling + // events in the time it took us to reach this point) + if (!libusb_event_handler_active(ctx)) { + // whoever was handling events is no longer doing so, try again + libusb_unlock_event_waiters(ctx); + goto retry; + } + + libusb_wait_for_event(ctx, NULL); + } + libusb_unlock_event_waiters(ctx); +} +printf("completed!\n"); +\endcode + * + * A naive look at the above code may suggest that this can only support + * one event waiter (hence a total of 2 competing threads, the other doing + * event handling), because the event waiter seems to have taken the event + * waiters lock while waiting for an event. However, the system does support + * multiple event waiters, because libusb_wait_for_event() actually drops + * the lock while waiting, and reacquires it before continuing. + * + * We have now implemented code which can dynamically handle situations where + * nobody is handling events (so we should do it ourselves), and it can also + * handle situations where another thread is doing event handling (so we can + * piggyback onto them). It is also equipped to handle a combination of + * the two, for example, another thread is doing event handling, but for + * whatever reason it stops doing so before our condition is met, so we take + * over the event handling. + * + * Four functions were introduced in the above pseudo-code. Their importance + * should be apparent from the code shown above. + * -# libusb_try_lock_events() is a non-blocking function which attempts + * to acquire the events lock but returns a failure code if it is contended. + * -# libusb_event_handling_ok() checks that libusb is still happy for your + * thread to be performing event handling. Sometimes, libusb needs to + * interrupt the event handler, and this is how you can check if you have + * been interrupted. If this function returns 0, the correct behaviour is + * for you to give up the event handling lock, and then to repeat the cycle. + * The following libusb_try_lock_events() will fail, so you will become an + * events waiter. For more information on this, read \ref fullstory below. + * -# libusb_handle_events_locked() is a variant of + * libusb_handle_events_timeout() that you can call while holding the + * events lock. libusb_handle_events_timeout() itself implements similar + * logic to the above, so be sure not to call it when you are + * "working behind libusb's back", as is the case here. + * -# libusb_event_handler_active() determines if someone is currently + * holding the events lock + * + * You might be wondering why there is no function to wake up all threads + * blocked on libusb_wait_for_event(). This is because libusb can do this + * internally: it will wake up all such threads when someone calls + * libusb_unlock_events() or when a transfer completes (at the point after its + * callback has returned). + * + * \subsection fullstory The full story + * + * The above explanation should be enough to get you going, but if you're + * really thinking through the issues then you may be left with some more + * questions regarding libusb's internals. If you're curious, read on, and if + * not, skip to the next section to avoid confusing yourself! + * + * The immediate question that may spring to mind is: what if one thread + * modifies the set of file descriptors that need to be polled while another + * thread is doing event handling? + * + * There are 2 situations in which this may happen. + * -# libusb_open() will add another file descriptor to the poll set, + * therefore it is desirable to interrupt the event handler so that it + * restarts, picking up the new descriptor. + * -# libusb_close() will remove a file descriptor from the poll set. There + * are all kinds of race conditions that could arise here, so it is + * important that nobody is doing event handling at this time. + * + * libusb handles these issues internally, so application developers do not + * have to stop their event handlers while opening/closing devices. Here's how + * it works, focusing on the libusb_close() situation first: + * + * -# During initialization, libusb opens an internal pipe, and it adds the read + * end of this pipe to the set of file descriptors to be polled. + * -# During libusb_close(), libusb writes some dummy data on this event pipe. + * This immediately interrupts the event handler. libusb also records + * internally that it is trying to interrupt event handlers for this + * high-priority event. + * -# At this point, some of the functions described above start behaving + * differently: + * - libusb_event_handling_ok() starts returning 1, indicating that it is NOT + * OK for event handling to continue. + * - libusb_try_lock_events() starts returning 1, indicating that another + * thread holds the event handling lock, even if the lock is uncontended. + * - libusb_event_handler_active() starts returning 1, indicating that + * another thread is doing event handling, even if that is not true. + * -# The above changes in behaviour result in the event handler stopping and + * giving up the events lock very quickly, giving the high-priority + * libusb_close() operation a "free ride" to acquire the events lock. All + * threads that are competing to do event handling become event waiters. + * -# With the events lock held inside libusb_close(), libusb can safely remove + * a file descriptor from the poll set, in the safety of knowledge that + * nobody is polling those descriptors or trying to access the poll set. + * -# After obtaining the events lock, the close operation completes very + * quickly (usually a matter of milliseconds) and then immediately releases + * the events lock. + * -# At the same time, the behaviour of libusb_event_handling_ok() and friends + * reverts to the original, documented behaviour. + * -# The release of the events lock causes the threads that are waiting for + * events to be woken up and to start competing to become event handlers + * again. One of them will succeed; it will then re-obtain the list of poll + * descriptors, and USB I/O will then continue as normal. + * + * libusb_open() is similar, and is actually a more simplistic case. Upon a + * call to libusb_open(): + * + * -# The device is opened and a file descriptor is added to the poll set. + * -# libusb sends some dummy data on the event pipe, and records that it + * is trying to modify the poll descriptor set. + * -# The event handler is interrupted, and the same behaviour change as for + * libusb_close() takes effect, causing all event handling threads to become + * event waiters. + * -# The libusb_open() implementation takes its free ride to the events lock. + * -# Happy that it has successfully paused the events handler, libusb_open() + * releases the events lock. + * -# The event waiter threads are all woken up and compete to become event + * handlers again. The one that succeeds will obtain the list of poll + * descriptors again, which will include the addition of the new device. + * + * \subsection concl Closing remarks + * + * The above may seem a little complicated, but hopefully I have made it clear + * why such complications are necessary. Also, do not forget that this only + * applies to applications that take libusb's file descriptors and integrate + * them into their own polling loops. + * + * You may decide that it is OK for your multi-threaded application to ignore + * some of the rules and locks detailed above, because you don't think that + * two threads can ever be polling the descriptors at the same time. If that + * is the case, then that's good news for you because you don't have to worry. + * But be careful here; remember that the synchronous I/O functions do event + * handling internally. If you have one thread doing event handling in a loop + * (without implementing the rules and locking semantics documented above) + * and another trying to send a synchronous USB transfer, you will end up with + * two threads monitoring the same descriptors, and the above-described + * undesirable behaviour occurring. The solution is for your polling thread to + * play by the rules; the synchronous I/O functions do so, and this will result + * in them getting along in perfect harmony. + * + * If you do have a dedicated thread doing event handling, it is perfectly + * legal for it to take the event handling lock for long periods of time. Any + * synchronous I/O functions you call from other threads will transparently + * fall back to the "event waiters" mechanism detailed above. The only + * consideration that your event handling thread must apply is the one related + * to libusb_event_handling_ok(): you must call this before every poll(), and + * give up the events lock if instructed. + */ + +int usbi_io_init(struct libusb_context *ctx) +{ + int r; + + usbi_mutex_init(&ctx->flying_transfers_lock); + usbi_mutex_init(&ctx->events_lock); + usbi_mutex_init(&ctx->event_waiters_lock); + usbi_cond_init(&ctx->event_waiters_cond); + usbi_mutex_init(&ctx->event_data_lock); + usbi_tls_key_create(&ctx->event_handling_key); + list_init(&ctx->flying_transfers); + list_init(&ctx->event_sources); + list_init(&ctx->removed_event_sources); + list_init(&ctx->hotplug_msgs); + list_init(&ctx->completed_transfers); + + r = usbi_create_event(&ctx->event); + if (r < 0) + goto err; + + r = usbi_add_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event), USBI_EVENT_POLL_EVENTS); + if (r < 0) + goto err_destroy_event; + +#ifdef HAVE_OS_TIMER + r = usbi_create_timer(&ctx->timer); + if (r == 0) { + 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(ctx, "timer not available for timeouts"); + } +#endif + + return 0; + +#ifdef HAVE_OS_TIMER +err_destroy_timer: + usbi_destroy_timer(&ctx->timer); + usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event)); +#endif +err_destroy_event: + usbi_destroy_event(&ctx->event); +err: + usbi_mutex_destroy(&ctx->flying_transfers_lock); + usbi_mutex_destroy(&ctx->events_lock); + usbi_mutex_destroy(&ctx->event_waiters_lock); + usbi_cond_destroy(&ctx->event_waiters_cond); + usbi_mutex_destroy(&ctx->event_data_lock); + usbi_tls_key_delete(ctx->event_handling_key); + return r; +} + +static void cleanup_removed_event_sources(struct libusb_context *ctx) +{ + struct usbi_event_source *ievent_source, *tmp; + + for_each_removed_event_source_safe(ctx, ievent_source, tmp) { + list_del(&ievent_source->list); + free(ievent_source); + } +} + +void usbi_io_exit(struct libusb_context *ctx) +{ +#ifdef HAVE_OS_TIMER + if (usbi_using_timer(ctx)) { + usbi_remove_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer)); + usbi_destroy_timer(&ctx->timer); + } +#endif + usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event)); + usbi_destroy_event(&ctx->event); + usbi_mutex_destroy(&ctx->flying_transfers_lock); + usbi_mutex_destroy(&ctx->events_lock); + usbi_mutex_destroy(&ctx->event_waiters_lock); + usbi_cond_destroy(&ctx->event_waiters_cond); + usbi_mutex_destroy(&ctx->event_data_lock); + usbi_tls_key_delete(ctx->event_handling_key); + cleanup_removed_event_sources(ctx); + free(ctx->event_data); +} + +static void calculate_timeout(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + unsigned int timeout = transfer->timeout; + + if (!timeout) { + TIMESPEC_CLEAR(&itransfer->timeout); + return; + } + + usbi_get_monotonic_time(&itransfer->timeout); + + itransfer->timeout.tv_sec += timeout / 1000U; + itransfer->timeout.tv_nsec += (timeout % 1000U) * 1000000L; + if (itransfer->timeout.tv_nsec >= NSEC_PER_SEC) { + ++itransfer->timeout.tv_sec; + itransfer->timeout.tv_nsec -= NSEC_PER_SEC; + } +} + +/** \ingroup libusb_asyncio + * Allocate a libusb transfer with a specified number of isochronous packet + * descriptors. The returned transfer is pre-initialized for you. When the new + * transfer is no longer needed, it should be freed with + * libusb_free_transfer(). + * + * Transfers intended for non-isochronous endpoints (e.g. control, bulk, + * interrupt) should specify an iso_packets count of zero. + * + * For transfers intended for isochronous endpoints, specify an appropriate + * number of packet descriptors to be allocated as part of the transfer. + * The returned transfer is not specially initialized for isochronous I/O; + * you are still required to set the + * \ref libusb_transfer::num_iso_packets "num_iso_packets" and + * \ref libusb_transfer::type "type" fields accordingly. + * + * It is safe to allocate a transfer with some isochronous packets and then + * use it on a non-isochronous endpoint. If you do this, ensure that at time + * of submission, num_iso_packets is 0 and that type is set appropriately. + * + * \param iso_packets number of isochronous packet descriptors to allocate. Must be non-negative. + * \returns a newly allocated transfer, or NULL on error + */ +DEFAULT_VISIBILITY +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( + int iso_packets) +{ + assert(iso_packets >= 0); + if (iso_packets < 0) + return NULL; + + size_t priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + size_t usbi_transfer_size = PTR_ALIGN(sizeof(struct usbi_transfer)); + size_t libusb_transfer_size = PTR_ALIGN(sizeof(struct libusb_transfer)); + size_t iso_packets_size = sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets; + size_t alloc_size = priv_size + usbi_transfer_size + libusb_transfer_size + iso_packets_size; + unsigned char *ptr = calloc(1, alloc_size); + if (!ptr) + return NULL; + + struct usbi_transfer *itransfer = (struct usbi_transfer *)(ptr + priv_size); + itransfer->priv = ptr; + usbi_mutex_init(&itransfer->lock); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + return transfer; +} + +/** \ingroup libusb_asyncio + * Free a transfer structure. This should be called for all transfers + * allocated with libusb_alloc_transfer(). + * + * If the \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER + * "LIBUSB_TRANSFER_FREE_BUFFER" flag is set and the transfer buffer is + * non-NULL, this function will also free the transfer buffer using the + * standard system memory allocator (e.g. free()). + * + * It is legal to call this function with a NULL transfer. In this case, + * the function will simply return safely. + * + * It is not legal to free an active transfer (one which has been submitted + * and has not yet completed). + * + * \param transfer the transfer to free + */ +void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) +{ + if (!transfer) + return; + + usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", (void *) transfer); + if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) + free(transfer->buffer); + + struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + usbi_mutex_destroy(&itransfer->lock); + if (itransfer->dev) + libusb_unref_device(itransfer->dev); + + unsigned char *ptr = USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer); + assert(ptr == itransfer->priv); + free(ptr); +} + +/* iterates through the flying transfers, and rearms the timer based on the + * next upcoming timeout. + * NB: flying_transfers_lock must be held when calling this. + * returns 0 on success or a LIBUSB_ERROR code on failure. + */ +#ifdef HAVE_OS_TIMER +static int arm_timer_for_next_timeout(struct libusb_context *ctx) +{ + struct usbi_transfer *itransfer; + + if (!usbi_using_timer(ctx)) + return 0; + + for_each_transfer(ctx, itransfer) { + struct timespec *cur_ts = &itransfer->timeout; + + /* if we've reached transfers of infinite timeout, then we have no + * arming to do */ + if (!TIMESPEC_IS_SET(cur_ts)) + break; + + /* act on first transfer that has not already been handled */ + if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { +#ifdef ENABLE_LOGGING + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "next timeout originally %ums", transfer->timeout); +#endif + return usbi_arm_timer(&ctx->timer, cur_ts); + } + } + + usbi_dbg(ctx, "no timeouts, disarming timer"); + return usbi_disarm_timer(&ctx->timer); +} +#else +static inline int arm_timer_for_next_timeout(struct libusb_context *ctx) +{ + UNUSED(ctx); + return 0; +} +#endif + +/* add a transfer to the (timeout-sorted) active transfers list. + * This function will return non 0 if fails to update the timer, + * in which case the transfer is *not* on the flying_transfers list. + * NB: flying_transfers_lock MUST be held when calling this. */ +static int add_to_flying_list(struct usbi_transfer *itransfer) +{ + struct usbi_transfer *cur; + struct timespec *timeout = &itransfer->timeout; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int r = 0; + int first = 1; + + calculate_timeout(itransfer); + + /* if we have no other flying transfers, start the list with this one */ + if (list_empty(&ctx->flying_transfers)) { + list_add(&itransfer->list, &ctx->flying_transfers); + goto out; + } + + /* if we have infinite timeout, append to end of list */ + if (!TIMESPEC_IS_SET(timeout)) { + list_add_tail(&itransfer->list, &ctx->flying_transfers); + /* first is irrelevant in this case */ + goto out; + } + + /* otherwise, find appropriate place in list */ + for_each_transfer(ctx, cur) { + /* find first timeout that occurs after the transfer in question */ + struct timespec *cur_ts = &cur->timeout; + + if (!TIMESPEC_IS_SET(cur_ts) || TIMESPEC_CMP(cur_ts, timeout, >)) { + list_add_tail(&itransfer->list, &cur->list); + goto out; + } + first = 0; + } + /* first is 0 at this stage (list not empty) */ + + /* otherwise we need to be inserted at the end */ + list_add_tail(&itransfer->list, &ctx->flying_transfers); +out: +#ifdef HAVE_OS_TIMER + 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 */ +#ifdef ENABLE_LOGGING + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)", + transfer->timeout); +#endif + r = usbi_arm_timer(&ctx->timer, timeout); + } +#else + UNUSED(first); +#endif + + if (r) + list_del(&itransfer->list); + + return r; +} + +/* remove a transfer from the active transfers list. + * This function will *always* remove the transfer from the + * flying_transfers list. It will return a LIBUSB_ERROR code + * if it fails to update the timer for the next timeout. + * NB: flying_transfers_lock MUST be held when calling this. */ +static int remove_from_flying_list(struct usbi_transfer *itransfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int rearm_timer; + int r = 0; + + rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) && + list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer); + list_del(&itransfer->list); + if (rearm_timer) + r = arm_timer_for_next_timeout(ctx); + + return r; +} + +/** \ingroup libusb_asyncio + * Submit a transfer. This function will fire off the USB transfer and then + * return immediately. + * + * \param transfer the transfer to submit + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if the transfer has already been submitted. + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported + * by the operating system. + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + struct libusb_context *ctx; + int r; + + 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", (void *) transfer); + + /* + * Important note on locking, this function takes / releases locks + * in the following order: + * take flying_transfers_lock + * take itransfer->lock + * clear transfer + * add to flying_transfers list + * release flying_transfers_lock + * submit transfer + * release itransfer->lock + * if submit failed: + * take flying_transfers_lock + * remove from flying_transfers list + * release flying_transfers_lock + * + * Note that it takes locks in the order a-b and then releases them + * in the same order a-b. This is somewhat unusual but not wrong, + * release order is not important as long as *all* locks are released + * before re-acquiring any locks. + * + * This means that the ordering of first releasing itransfer->lock + * and then re-acquiring the flying_transfers_list on error is + * important and must not be changed! + * + * This is done this way because when we take both locks we must always + * take flying_transfers_lock first to avoid ab-ba style deadlocks with + * the timeout handling and usbi_handle_disconnect paths. + * + * And we cannot release itransfer->lock before the submission is + * complete otherwise timeout handling for transfers with short + * timeouts may run before submission. + */ + usbi_mutex_lock(&ctx->flying_transfers_lock); + usbi_mutex_lock(&itransfer->lock); + if (itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&itransfer->lock); + return LIBUSB_ERROR_BUSY; + } + itransfer->transferred = 0; + itransfer->state_flags = 0; + itransfer->timeout_flags = 0; + r = add_to_flying_list(itransfer); + if (r) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&itransfer->lock); + return r; + } + /* + * We must release the flying transfers lock here, because with + * 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; + } + usbi_mutex_unlock(&itransfer->lock); + + if (r != LIBUSB_SUCCESS) { + usbi_mutex_lock(&ctx->flying_transfers_lock); + remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + } + + return r; +} + +/** \ingroup libusb_asyncio + * Asynchronously cancel a previously submitted transfer. + * This function returns immediately, but this does not indicate cancellation + * is complete. Your callback function will be invoked at some later time + * with a transfer status of + * \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. + + * - When built for macOS versions prior to 10.5, this function sends a + * \c ClearFeature(ENDPOINT_HALT) request for the transfer's endpoint. + * (Prior to libusb 1.0.27, this request was sent on all Darwin systems.) + * 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 + * macOS < 10.5 (or libusb < 1.0.27), 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 \ref LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, + * already complete, or already cancelled. + * \returns a LIBUSB_ERROR code on failure + */ +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(ctx, "transfer %p", (void *) transfer ); + usbi_mutex_lock(&itransfer->lock); + if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) + || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + r = usbi_backend.cancel_transfer(itransfer); + if (r < 0) { + if (r != LIBUSB_ERROR_NOT_FOUND && + r != LIBUSB_ERROR_NO_DEVICE) + usbi_err(ctx, "cancel transfer failed error %d", r); + else + usbi_dbg(ctx, "cancel transfer failed error %d", r); + + if (r == LIBUSB_ERROR_NO_DEVICE) + itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; + } + + itransfer->state_flags |= USBI_TRANSFER_CANCELLING; + +out: + usbi_mutex_unlock(&itransfer->lock); + return r; +} + +/** \ingroup libusb_asyncio + * Set a transfers bulk stream id. Note users are advised to use + * libusb_fill_bulk_stream_transfer() instead of calling this function + * directly. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to set the stream id for + * \param stream_id the stream id to set + * \see libusb_alloc_streams() + */ +void API_EXPORTED libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + + itransfer->stream_id = stream_id; +} + +/** \ingroup libusb_asyncio + * Get a transfers bulk stream id. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to get the stream id for + * \returns the stream id for the transfer + */ +uint32_t API_EXPORTED libusb_transfer_get_stream_id( + struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + + return itransfer->stream_id; +} + +/* Handle completion of a transfer (completion might be an error condition). + * This will invoke the user-supplied callback function, which may end up + * freeing the transfer. Therefore you cannot use the transfer structure + * after calling this function, and you should free all backend-specific + * data before calling it. + * Do not call this function with the usbi_transfer lock held. User-specified + * callback functions may attempt to directly resubmit the transfer, which + * will attempt to take the lock. */ +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + uint8_t flags; + int r; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + r = remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + if (r < 0) + usbi_err(ctx, "failed to set timer for next timeout"); + + usbi_mutex_lock(&itransfer->lock); + itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT; + usbi_mutex_unlock(&itransfer->lock); + + if (status == LIBUSB_TRANSFER_COMPLETED + && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { + int rqlen = transfer->length; + if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) + rqlen -= LIBUSB_CONTROL_SETUP_SIZE; + if (rqlen != itransfer->transferred) { + usbi_dbg(ctx, "interpreting short transfer as error"); + status = LIBUSB_TRANSFER_ERROR; + } + } + + flags = transfer->flags; + transfer->status = status; + transfer->actual_length = itransfer->transferred; + assert(transfer->actual_length >= 0); + usbi_dbg(ctx, "transfer %p has callback %p", + (void *) transfer, transfer->callback); + if (transfer->callback) { + libusb_lock_event_waiters (ctx); + transfer->callback(transfer); + libusb_unlock_event_waiters(ctx); + } + /* 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); + return r; +} + +/* Similar to usbi_handle_transfer_completion() but exclusively for transfers + * that were asynchronously cancelled. The same concerns w.r.t. freeing of + * transfers exist here. + * Do not call this function with the usbi_transfer lock held. User-specified + * callback functions may attempt to directly resubmit the transfer, which + * will attempt to take the lock. */ +int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + uint8_t timed_out; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + timed_out = itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT; + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + /* if the URB was cancelled due to timeout, report timeout to the user */ + if (timed_out) { + usbi_dbg(ctx, "detected timeout cancellation"); + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT); + } + + /* otherwise its a normal async cancel */ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_CANCELLED); +} + +/* Add a completed transfer to the completed_transfers list of the + * context and signal the event. The backend's handle_transfer_completion() + * function will be called the next time an event handler runs. */ +void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) +{ + struct libusb_device *dev = itransfer->dev; + + if (dev) { + struct libusb_context *ctx = DEVICE_CTX(dev); + unsigned int event_flags; + + usbi_mutex_lock(&ctx->event_data_lock); + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_TRANSFER_COMPLETED; + list_add_tail(&itransfer->completed_list, &ctx->completed_transfers); + if (!event_flags) + usbi_signal_event(&ctx->event); + usbi_mutex_unlock(&ctx->event_data_lock); + } +} + +/** \ingroup libusb_poll + * Attempt to acquire the event handling lock. This lock is used to ensure that + * only one thread is monitoring libusb event sources at any one time. + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * While holding this lock, you are trusted to actually be handling events. + * If you are no longer handling events, you must call libusb_unlock_events() + * as soon as possible. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 if the lock was obtained successfully + * \returns 1 if the lock was not obtained (i.e. another thread holds the lock) + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) +{ + int r; + unsigned int ru; + + ctx = usbi_get_context(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * start event handling */ + usbi_mutex_lock(&ctx->event_data_lock); + ru = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (ru) { + usbi_dbg(ctx, "someone else is closing a device"); + return 1; + } + + r = usbi_mutex_trylock(&ctx->events_lock); + if (!r) + return 1; + + ctx->event_handler_active = 1; + return 0; +} + +/** \ingroup libusb_poll + * Acquire the event handling lock, blocking until successful acquisition if + * it is contended. This lock is used to ensure that only one thread is + * monitoring libusb event sources at any one time. + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * While holding this lock, you are trusted to actually be handling events. + * If you are no longer handling events, you must call libusb_unlock_events() + * as soon as possible. + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_lock_events(libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + usbi_mutex_lock(&ctx->events_lock); + ctx->event_handler_active = 1; +} + +/** \ingroup libusb_poll + * Release the lock previously acquired with libusb_try_lock_events() or + * libusb_lock_events(). Releasing this lock will wake up any threads blocked + * on libusb_wait_for_event(). + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_unlock_events(libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + ctx->event_handler_active = 0; + usbi_mutex_unlock(&ctx->events_lock); + + /* FIXME: perhaps we should be a bit more efficient by not broadcasting + * the availability of the events lock when we are modifying pollfds + * (check ctx->device_close)? */ + usbi_mutex_lock(&ctx->event_waiters_lock); + usbi_cond_broadcast(&ctx->event_waiters_cond); + usbi_mutex_unlock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Determine if it is still OK for this thread to be doing event handling. + * + * Sometimes, libusb needs to temporarily pause all event handlers, and this + * is the function you should use before polling file descriptors to see if + * this is the case. + * + * If this function instructs your thread to give up the events lock, you + * should just continue the usual logic that is documented in \ref libusb_mtasync. + * On the next iteration, your thread will fail to obtain the events lock, + * and will hence become an event waiter. + * + * This function should be called while the events lock is held: you don't + * need to worry about the results of this function if your thread is not + * the current event handler. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 1 if event handling can start or continue + * \returns 0 if this thread must give up the events lock + * \ref fullstory "Multi-threaded I/O: the full story" + */ +int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) +{ + unsigned int r; + + ctx = usbi_get_context(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * continue event handling */ + usbi_mutex_lock(&ctx->event_data_lock); + r = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (r) { + usbi_dbg(ctx, "someone else is closing a device"); + return 0; + } + + return 1; +} + + +/** \ingroup libusb_poll + * Determine if an active thread is handling events (i.e. if anyone is holding + * the event handling lock). + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 1 if a thread is handling events + * \returns 0 if there are no threads currently handling events + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) +{ + unsigned int r; + + ctx = usbi_get_context(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * start event handling -- indicate that event handling is happening */ + usbi_mutex_lock(&ctx->event_data_lock); + r = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (r) { + usbi_dbg(ctx, "someone else is closing a device"); + return 1; + } + + return ctx->event_handler_active; +} + +/** \ingroup libusb_poll + * Interrupt any active thread that is handling events. This is mainly useful + * for interrupting a dedicated event handling thread when an application + * wishes to call libusb_exit(). + * + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) +{ + unsigned int event_flags; + + usbi_dbg(ctx, " "); + + ctx = usbi_get_context(ctx); + usbi_mutex_lock(&ctx->event_data_lock); + + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_USER_INTERRUPT; + if (!event_flags) + usbi_signal_event(&ctx->event); + + usbi_mutex_unlock(&ctx->event_data_lock); +} + +/** \ingroup libusb_poll + * Acquire the event waiters lock. This lock is designed to be obtained under + * the situation where you want to be aware when events are completed, but + * some other thread is event handling so calling libusb_handle_events() is not + * allowed. + * + * You then obtain this lock, re-check that another thread is still handling + * events, then call libusb_wait_for_event(). + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly, + * and may potentially be handling events from 2 threads simultaneously. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + usbi_mutex_lock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Release the event waiters lock. + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + usbi_mutex_unlock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Wait for another thread to signal completion of an event. Must be called + * with the event waiters lock held, see libusb_lock_event_waiters(). + * + * This function will block until any of the following conditions are met: + * -# The timeout expires + * -# A transfer completes + * -# A thread releases the event handling lock through libusb_unlock_events() + * + * Condition 1 is obvious. Condition 2 unblocks your thread after + * the callback for the transfer has completed. Condition 3 is important + * because it means that the thread that was previously handling events is no + * longer doing so, so if any events are to complete, another thread needs to + * step up and start event handling. + * + * This function releases the event waiters lock before putting your thread + * to sleep, and reacquires the lock as it is being woken up. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv maximum timeout for this blocking function. A NULL value + * indicates unlimited timeout. + * \returns 0 after a transfer completes or another thread stops event handling + * \returns 1 if the timeout expired + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) +{ + int r; + + ctx = usbi_get_context(ctx); + if (!tv) { + usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock); + return 0; + } + + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + + r = usbi_cond_timedwait(&ctx->event_waiters_cond, + &ctx->event_waiters_lock, tv); + if (r < 0) + return r == LIBUSB_ERROR_TIMEOUT; + + return 0; +} + +/* NB: flying_transfers_lock must be held when calling this */ +static void handle_timeout(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int r; + + itransfer->timeout_flags |= USBI_TRANSFER_TIMEOUT_HANDLED; + r = libusb_cancel_transfer(transfer); + if (r == LIBUSB_SUCCESS) + itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + else + usbi_warn(TRANSFER_CTX(transfer), + "async cancel failed %d", r); +} + +/* NB: flying_transfers_lock must be held when calling this */ +static void handle_timeouts_locked(struct libusb_context *ctx) +{ + struct timespec systime; + struct usbi_transfer *itransfer; + + if (list_empty(&ctx->flying_transfers)) + return; + + /* get current time */ + usbi_get_monotonic_time(&systime); + + /* iterate through flying transfers list, finding all transfers that + * have expired timeouts */ + for_each_transfer(ctx, itransfer) { + struct timespec *cur_ts = &itransfer->timeout; + + /* if we've reached transfers of infinite timeout, we're all done */ + if (!TIMESPEC_IS_SET(cur_ts)) + return; + + /* ignore timeouts we've already handled */ + if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if transfer has non-expired timeout, nothing more to do */ + if (TIMESPEC_CMP(cur_ts, &systime, >)) + return; + + /* otherwise, we've got an expired timeout to handle */ + handle_timeout(itransfer); + } +} + +static void handle_timeouts(struct libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + usbi_mutex_lock(&ctx->flying_transfers_lock); + handle_timeouts_locked(ctx); + usbi_mutex_unlock(&ctx->flying_transfers_lock); +} + +static int handle_event_trigger(struct libusb_context *ctx) +{ + struct list_head hotplug_msgs; + int hotplug_event = 0; + int r = 0; + + usbi_dbg(ctx, "event triggered"); + + list_init(&hotplug_msgs); + + /* take the the event data lock while processing events */ + usbi_mutex_lock(&ctx->event_data_lock); + + /* check if someone modified the event sources */ + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) + usbi_dbg(ctx, "someone updated the event sources"); + + if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { + 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(ctx, "someone is closing a device"); + + /* check for any pending hotplug messages */ + if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) { + 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); + } + + /* complete any pending transfers */ + if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) { + struct usbi_transfer *itransfer, *tmp; + struct list_head completed_transfers; + + assert(!list_empty(&ctx->completed_transfers)); + list_cut(&completed_transfers, &ctx->completed_transfers); + usbi_mutex_unlock(&ctx->event_data_lock); + + __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) { + list_del(&itransfer->completed_list); + r = usbi_backend.handle_transfer_completion(itransfer); + if (r) { + usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r); + break; + } + } + + usbi_mutex_lock(&ctx->event_data_lock); + if (!list_empty(&completed_transfers)) { + /* an error occurred, put the remaining transfers back on the list */ + list_splice_front(&completed_transfers, &ctx->completed_transfers); + } else if (list_empty(&ctx->completed_transfers)) { + ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED; + } + } + + /* if no further pending events, clear the event */ + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); + + usbi_mutex_unlock(&ctx->event_data_lock); + + /* process the hotplug events, if any */ + if (hotplug_event) + usbi_hotplug_process(ctx, &hotplug_msgs); + + return r; +} + +#ifdef HAVE_OS_TIMER +static int handle_timer_trigger(struct libusb_context *ctx) +{ + int r; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + + /* process the timeout that just happened */ + handle_timeouts_locked(ctx); + + /* arm for next timeout */ + r = arm_timer_for_next_timeout(ctx); + + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + return r; +} +#endif + +/* do the actual event handling. assumes that no other thread is concurrently + * doing the same thing. */ +static int handle_events(struct libusb_context *ctx, struct timeval *tv) +{ + struct usbi_reported_events reported_events; + int r, timeout_ms; + + /* prevent attempts to recursively handle events (e.g. calling into + * libusb_handle_events() from within a hotplug or transfer callback) */ + if (usbi_handling_events(ctx)) + return LIBUSB_ERROR_BUSY; + + /* only reallocate the event source data when the list of event sources has + * been modified since the last handle_events(), otherwise reuse them to + * save the additional overhead */ + usbi_mutex_lock(&ctx->event_data_lock); + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) { + usbi_dbg(ctx, "event sources modified, reallocating event data"); + + /* free anything removed since we last ran */ + cleanup_removed_event_sources(ctx); + + r = usbi_alloc_event_data(ctx); + if (r) { + usbi_mutex_unlock(&ctx->event_data_lock); + return r; + } + + /* reset the flag now that we have the updated list */ + ctx->event_flags &= ~USBI_EVENT_EVENT_SOURCES_MODIFIED; + + /* if no further pending events, clear the event so that we do + * not immediately return from the wait function */ + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); + } + usbi_mutex_unlock(&ctx->event_data_lock); + + timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); + + /* round up to next millisecond */ + if (tv->tv_usec % 1000) + timeout_ms++; + + reported_events.event_bits = 0; + + usbi_start_event_handling(ctx); + + r = usbi_wait_for_events(ctx, &reported_events, timeout_ms); + if (r != LIBUSB_SUCCESS) { + if (r == LIBUSB_ERROR_TIMEOUT) { + handle_timeouts(ctx); + r = LIBUSB_SUCCESS; + } + goto done; + } + + if (reported_events.event_triggered) { + r = handle_event_trigger(ctx); + if (r) { + /* return error code */ + goto done; + } + } + +#ifdef HAVE_OS_TIMER + if (reported_events.timer_triggered) { + r = handle_timer_trigger(ctx); + if (r) { + /* return error code */ + goto done; + } + } +#endif + + if (!reported_events.num_ready) + goto done; + + r = usbi_backend.handle_events(ctx, reported_events.event_data, + reported_events.event_data_count, reported_events.num_ready); + if (r) + usbi_err(ctx, "backend handle_events failed with error %d", r); + +done: + usbi_end_event_handling(ctx); + return r; +} + +/* returns the smallest of: + * 1. timeout of next URB + * 2. user-supplied timeout + * returns 1 if there is an already-expired timeout, otherwise returns 0 + * and populates out + */ +static int get_next_timeout(libusb_context *ctx, struct timeval *tv, + struct timeval *out) +{ + struct timeval timeout; + int r = libusb_get_next_timeout(ctx, &timeout); + if (r) { + /* timeout already expired? */ + if (!timerisset(&timeout)) + return 1; + + /* choose the smallest of next URB timeout or user specified timeout */ + if (timercmp(&timeout, tv, <)) + *out = timeout; + else + *out = *tv; + } else { + *out = *tv; + } + return 0; +} + +/** \ingroup libusb_poll + * Handle any pending events. + * + * libusb determines "pending events" by checking if any timeouts have expired + * and by checking the set of file descriptors for activity. + * + * If a zero timeval is passed, this function will handle any already-pending + * events and then immediately return in non-blocking style. + * + * If a non-zero timeval is passed and no events are currently pending, this + * function will block waiting for events to handle up until the specified + * timeout. If an event arrives or a signal is raised, this function will + * return early. + * + * If the parameter completed is not NULL then after obtaining the event + * handling lock this function will return immediately if the integer + * pointed to is not 0. This allows for race free waiting for the completion + * of a specific transfer. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or an all zero + * timeval struct for non-blocking mode + * \param completed pointer to completion integer to check, or NULL + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns another LIBUSB_ERROR code on other failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed) +{ + int r; + struct timeval poll_timeout; + + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + + ctx = usbi_get_context(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); + if (r) { + /* timeout already expired */ + handle_timeouts(ctx); + return 0; + } + +retry: + if (libusb_try_lock_events(ctx) == 0) { + if (completed == NULL || !*completed) { + /* we obtained the event lock: do our own event handling */ + usbi_dbg(ctx, "doing our own event handling"); + r = handle_events(ctx, &poll_timeout); + } + libusb_unlock_events(ctx); + return r; + } + + /* another thread is doing event handling. wait for thread events that + * notify event completion. */ + libusb_lock_event_waiters(ctx); + + if (completed && *completed) + goto already_done; + + if (!libusb_event_handler_active(ctx)) { + /* 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(ctx, "event handler was active but went away, retrying"); + goto retry; + } + + usbi_dbg(ctx, "another thread is doing event handling"); + r = libusb_wait_for_event(ctx, &poll_timeout); + +already_done: + libusb_unlock_event_waiters(ctx); + + if (r < 0) + return r; + else if (r == 1) + handle_timeouts(ctx); + return 0; +} + +/** \ingroup libusb_poll + * Handle any pending events + * + * Like libusb_handle_events_timeout_completed(), but without the completed + * parameter, calling this function is equivalent to calling + * libusb_handle_events_timeout_completed() with a NULL completed parameter. + * + * This function is kept primarily for backwards compatibility. + * All new code should call libusb_handle_events_completed() or + * libusb_handle_events_timeout_completed() to avoid race conditions. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or an all zero + * timeval struct for non-blocking mode + * \returns 0 on success, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv) +{ + return libusb_handle_events_timeout_completed(ctx, tv, NULL); +} + +/** \ingroup libusb_poll + * Handle any pending events in blocking mode. There is currently a timeout + * hard-coded at 60 seconds but we plan to make it unlimited in future. For + * finer control over whether this function is blocking or non-blocking, or + * for control over the timeout, use libusb_handle_events_timeout_completed() + * instead. + * + * This function is kept primarily for backwards compatibility. + * All new code should call libusb_handle_events_completed() or + * libusb_handle_events_timeout_completed() to avoid race conditions. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 on success, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_handle_events(libusb_context *ctx) +{ + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + return libusb_handle_events_timeout_completed(ctx, &tv, NULL); +} + +/** \ingroup libusb_poll + * Handle any pending events in blocking mode. + * + * Like libusb_handle_events(), with the addition of a completed parameter + * to allow for race free waiting for the completion of a specific transfer. + * + * See libusb_handle_events_timeout_completed() for details on the completed + * parameter. + * + * \param ctx the context to operate on, or NULL for the default context + * \param completed pointer to completion integer to check, or NULL + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, + int *completed) +{ + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + return libusb_handle_events_timeout_completed(ctx, &tv, completed); +} + +/** \ingroup libusb_poll + * Handle any pending events by polling file descriptors, without checking if + * any other threads are already doing so. Must be called with the event lock + * held, see libusb_lock_events(). + * + * This function is designed to be called under the situation where you have + * taken the event lock and are calling poll()/select() directly on libusb's + * file descriptors (as opposed to using libusb_handle_events() or similar). + * You detect events on libusb's descriptors, so you then call this function + * with a zero timeout value (while still holding the event lock). + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or zero for + * non-blocking mode + * \returns 0 on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns another LIBUSB_ERROR code on other failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv) +{ + int r; + struct timeval poll_timeout; + + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + + ctx = usbi_get_context(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); + if (r) { + /* timeout already expired */ + handle_timeouts(ctx); + return 0; + } + + return handle_events(ctx, &poll_timeout); +} + +/** \ingroup libusb_poll + * Determines whether your application must apply special timing considerations + * when monitoring libusb's file descriptors. + * + * This function is only useful for applications which retrieve and poll + * libusb's file descriptors in their own main loop (\ref libusb_pollmain). + * + * Ordinarily, libusb's event handler needs to be called into at specific + * moments in time (in addition to times when there is activity on the file + * descriptor set). The usual approach is to use libusb_get_next_timeout() + * to learn about when the next timeout occurs, and to adjust your + * poll()/select() timeout accordingly so that you can make a call into the + * library at that time. + * + * Some platforms supported by libusb do not come with this baggage - any + * events relevant to timing will be represented by activity on the file + * descriptor set, and libusb_get_next_timeout() will always return 0. + * This function allows you to detect whether you are running on such a + * platform. + * + * Since v1.0.5. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 if you must call into libusb at times determined by + * libusb_get_next_timeout(), or 1 if all timeout events are handled internally + * or through regular activity on the file descriptors. + * \ref libusb_pollmain "Polling libusb file descriptors for event handling" + */ +int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) +{ + ctx = usbi_get_context(ctx); + return usbi_using_timer(ctx); +} + +/** \ingroup libusb_poll + * Determine the next internal timeout that libusb needs to handle. You only + * need to use this function if you are calling poll() or select() or similar + * on libusb's file descriptors yourself - you do not need to use it if you + * are calling libusb_handle_events() or a variant directly. + * + * You should call this function in your main loop in order to determine how + * long to wait for select() or poll() to return results. libusb needs to be + * called into at this timeout, so you should use it as an upper bound on + * your select() or poll() call. + * + * When the timeout has expired, call into libusb_handle_events_timeout() + * (perhaps in non-blocking mode) so that libusb can handle the timeout. + * + * This function may return 1 (success) and an all-zero timeval. If this is + * the case, it indicates that libusb has a timeout that has already expired + * so you should call libusb_handle_events_timeout() or similar immediately. + * A return code of 0 indicates that there are no pending timeouts. + * + * On some platforms, this function will always returns 0 (no pending + * timeouts). See \ref polltime. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv output location for a relative time against the current + * clock in which libusb must be called into in order to process timeout events + * \returns 0 if there are no pending timeouts, 1 if a timeout was returned, + * or \ref LIBUSB_ERROR_OTHER on failure + */ +int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv) +{ + struct usbi_transfer *itransfer; + struct timespec systime; + struct timespec next_timeout = { 0, 0 }; + + ctx = usbi_get_context(ctx); + if (usbi_using_timer(ctx)) + return 0; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + if (list_empty(&ctx->flying_transfers)) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_dbg(ctx, "no URBs, no timeout!"); + return 0; + } + + /* find next transfer which hasn't already been processed as timed out */ + for_each_transfer(ctx, itransfer) { + if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if we've reached transfers of infinite timeout, we're done looking */ + if (!TIMESPEC_IS_SET(&itransfer->timeout)) + break; + + next_timeout = itransfer->timeout; + break; + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (!TIMESPEC_IS_SET(&next_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(ctx, "first timeout already expired"); + timerclear(tv); + } else { + TIMESPEC_SUB(&next_timeout, &systime, &next_timeout); + TIMESPEC_TO_TIMEVAL(tv, &next_timeout); + usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec); + } + + return 1; +} + +/** \ingroup libusb_poll + * Register notification functions for file descriptor additions/removals. + * These functions will be invoked for every new or removed file descriptor + * that libusb uses as an event source. + * + * To remove notifiers, pass NULL values for the function pointers. + * + * Note that file descriptors may have been added even before you register + * these notifiers (e.g. at libusb_init_context() time). + * + * Additionally, note that the removal notifier may be called during + * libusb_exit() (e.g. when it is closing file descriptors that were opened + * and added to the poll set at libusb_init_context() time). If you don't want this, + * remove the notifiers immediately before calling libusb_exit(). + * + * \param ctx the context to operate on, or NULL for the default context + * \param added_cb pointer to function for addition notifications + * \param removed_cb pointer to function for removal notifications + * \param user_data User data to be passed back to callbacks (useful for + * passing context information) + */ +void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data) +{ +#if !defined(PLATFORM_WINDOWS) + ctx = usbi_get_context(ctx); + ctx->fd_added_cb = added_cb; + ctx->fd_removed_cb = removed_cb; + ctx->fd_cb_user_data = user_data; +#else + usbi_err(ctx, "external polling of libusb's internal event sources " \ + "is not yet supported on Windows"); + UNUSED(added_cb); + UNUSED(removed_cb); + UNUSED(user_data); +#endif +} + +/* + * Interrupt the iteration of the event handling thread, so that it picks + * up the event source change. Callers of this function must hold the event_data_lock. + */ +static void usbi_event_source_notification(struct libusb_context *ctx) +{ + unsigned int event_flags; + + /* Record that there is a new poll fd. + * Only signal an event if there are no prior pending events. */ + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED; + if (!event_flags) + usbi_signal_event(&ctx->event); +} + +/* Add an event source to the list of event sources to be monitored. + * poll_events should be specified as a bitmask of events passed to poll(), e.g. + * POLLIN and/or POLLOUT. */ +int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, short poll_events) +{ + struct usbi_event_source *ievent_source = malloc(sizeof(*ievent_source)); + + if (!ievent_source) + return LIBUSB_ERROR_NO_MEM; + + 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); + list_add_tail(&ievent_source->list, &ctx->event_sources); + usbi_event_source_notification(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + +#if !defined(PLATFORM_WINDOWS) + if (ctx->fd_added_cb) + ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data); +#endif + + return 0; +} + +/* Remove an event source from the list of event sources to be monitored. */ +void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle) +{ + struct usbi_event_source *ievent_source; + int found = 0; + + 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) { + found = 1; + break; + } + } + + if (!found) { + usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle); + usbi_mutex_unlock(&ctx->event_data_lock); + return; + } + + list_del(&ievent_source->list); + list_add_tail(&ievent_source->list, &ctx->removed_event_sources); + usbi_event_source_notification(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + +#if !defined(PLATFORM_WINDOWS) + if (ctx->fd_removed_cb) + ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data); +#endif +} + +/** \ingroup libusb_poll + * Retrieve a list of file descriptors that should be polled by your main loop + * as libusb event sources. + * + * The returned list is NULL-terminated and should be freed with libusb_free_pollfds() + * when done. The actual list contents must not be touched. + * + * As file descriptors are a Unix-specific concept, this function is not + * available on Windows and will always return NULL. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns a NULL-terminated list of libusb_pollfd structures + * \returns NULL on error + * \returns NULL on platforms where the functionality is not available + */ +DEFAULT_VISIBILITY +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx) +{ +#if !defined(PLATFORM_WINDOWS) + struct libusb_pollfd **ret = NULL; + struct usbi_event_source *ievent_source; + size_t i; + + static_assert(sizeof(struct usbi_event_source_data) == sizeof(struct libusb_pollfd), + "mismatch between usbi_event_source_data and libusb_pollfd sizes"); + + ctx = usbi_get_context(ctx); + + usbi_mutex_lock(&ctx->event_data_lock); + + i = 0; + for_each_event_source(ctx, ievent_source) + i++; + + ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); + if (!ret) + goto out; + + i = 0; + for_each_event_source(ctx, ievent_source) + ret[i++] = (struct libusb_pollfd *)ievent_source; + +out: + usbi_mutex_unlock(&ctx->event_data_lock); + return (const struct libusb_pollfd **)ret; +#else + usbi_err(ctx, "external polling of libusb's internal event sources " \ + "is not yet supported on Windows"); + return NULL; +#endif +} + +/** \ingroup libusb_poll + * Free a list of libusb_pollfd structures. This should be called for all + * pollfd lists allocated with libusb_get_pollfds(). + * + * Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104 + * + * It is legal to call this function with a NULL pollfd list. In this case, + * the function will simply do nothing. + * + * \param pollfds the list of libusb_pollfd structures to free + */ +void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds) +{ +#if !defined(PLATFORM_WINDOWS) + free((void *)pollfds); +#else + UNUSED(pollfds); +#endif +} + +/* Backends may call this from handle_events to report disconnection of a + * device. This function ensures transfers get cancelled appropriately. + * Callers of this function must hold the events_lock. + */ +void usbi_handle_disconnect(struct libusb_context *ctx, struct libusb_device_handle *dev_handle) +{ + struct usbi_transfer *cur; + struct usbi_transfer *to_cancel; + + 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 + * status code. + * + * when we find a transfer for this device on the list, there are two + * possible scenarios: + * 1. the transfer is currently in-flight, in which case we terminate the + * transfer here + * 2. the transfer has been added to the flying transfer list by + * libusb_submit_transfer, has failed to submit and + * libusb_submit_transfer is waiting for us to release the + * flying_transfers_lock to remove it, so we ignore it + */ + + while (1) { + to_cancel = NULL; + usbi_mutex_lock(&ctx->flying_transfers_lock); + for_each_transfer(ctx, cur) { + struct libusb_transfer *cur_transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur); + if (cur_transfer->dev_handle == dev_handle) { + usbi_mutex_lock(&cur->lock); + if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) + to_cancel = cur; + usbi_mutex_unlock(&cur->lock); + + if (to_cancel) + break; + } + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (!to_cancel) + break; + +#ifdef ENABLE_LOGGING + struct libusb_transfer *transfer_to_cancel = USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel); + usbi_dbg(ctx, "cancelling transfer %p from disconnect", + (void *) transfer_to_cancel); +#endif + + usbi_mutex_lock(&to_cancel->lock); + usbi_backend.clear_transfer_priv(to_cancel); + usbi_mutex_unlock(&to_cancel->lock); + usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); + } +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.def b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.def new file mode 100644 index 00000000..eacdb81f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.def @@ -0,0 +1,209 @@ +LIBRARY "libusb-1.0.dll" +EXPORTS + libusb_alloc_streams + libusb_alloc_streams@16 = libusb_alloc_streams + libusb_alloc_transfer + libusb_alloc_transfer@4 = libusb_alloc_transfer + libusb_attach_kernel_driver + libusb_attach_kernel_driver@8 = libusb_attach_kernel_driver + libusb_bulk_transfer + libusb_bulk_transfer@24 = libusb_bulk_transfer + libusb_cancel_transfer + libusb_cancel_transfer@4 = libusb_cancel_transfer + libusb_claim_interface + libusb_claim_interface@8 = libusb_claim_interface + libusb_clear_halt + libusb_clear_halt@8 = libusb_clear_halt + libusb_close + libusb_close@4 = libusb_close + libusb_control_transfer + libusb_control_transfer@32 = libusb_control_transfer + libusb_detach_kernel_driver + libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver + libusb_dev_mem_alloc + libusb_dev_mem_alloc@8 = libusb_dev_mem_alloc + libusb_dev_mem_free + libusb_dev_mem_free@12 = libusb_dev_mem_free + libusb_endpoint_set_raw_io + libusb_endpoint_set_raw_io@12 = libusb_endpoint_set_raw_io + libusb_endpoint_supports_raw_io + libusb_endpoint_supports_raw_io@8 = libusb_endpoint_supports_raw_io + libusb_error_name + libusb_error_name@4 = libusb_error_name + libusb_event_handler_active + libusb_event_handler_active@4 = libusb_event_handler_active + libusb_event_handling_ok + libusb_event_handling_ok@4 = libusb_event_handling_ok + libusb_exit + libusb_exit@4 = libusb_exit + libusb_free_bos_descriptor + libusb_free_bos_descriptor@4 = libusb_free_bos_descriptor + libusb_free_config_descriptor + libusb_free_config_descriptor@4 = libusb_free_config_descriptor + libusb_free_container_id_descriptor + libusb_free_container_id_descriptor@4 = libusb_free_container_id_descriptor + libusb_free_device_list + libusb_free_device_list@8 = libusb_free_device_list + libusb_free_interface_association_descriptors + libusb_free_interface_association_descriptors@4 = libusb_free_interface_association_descriptors + libusb_free_platform_descriptor + libusb_free_platform_descriptor@4 = libusb_free_platform_descriptor + libusb_free_pollfds + libusb_free_pollfds@4 = libusb_free_pollfds + libusb_free_ss_endpoint_companion_descriptor + libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor + libusb_free_ss_usb_device_capability_descriptor + libusb_free_ss_usb_device_capability_descriptor@4 = libusb_free_ss_usb_device_capability_descriptor + libusb_free_ssplus_usb_device_capability_descriptor + libusb_free_ssplus_usb_device_capability_descriptor@4 = libusb_free_ssplus_usb_device_capability_descriptor + libusb_free_streams + libusb_free_streams@12 = libusb_free_streams + libusb_free_transfer + libusb_free_transfer@4 = libusb_free_transfer + libusb_free_usb_2_0_extension_descriptor + libusb_free_usb_2_0_extension_descriptor@4 = libusb_free_usb_2_0_extension_descriptor + libusb_get_active_config_descriptor + libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor + libusb_get_active_interface_association_descriptors + libusb_get_active_interface_association_descriptors@8 = libusb_get_active_interface_association_descriptors + libusb_get_bos_descriptor + libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor + libusb_get_bus_number + libusb_get_bus_number@4 = libusb_get_bus_number + libusb_get_config_descriptor + libusb_get_config_descriptor@12 = libusb_get_config_descriptor + libusb_get_config_descriptor_by_value + libusb_get_config_descriptor_by_value@12 = libusb_get_config_descriptor_by_value + libusb_get_configuration + libusb_get_configuration@8 = libusb_get_configuration + libusb_get_container_id_descriptor + libusb_get_container_id_descriptor@12 = libusb_get_container_id_descriptor + libusb_get_device + libusb_get_device@4 = libusb_get_device + libusb_get_device_address + libusb_get_device_address@4 = libusb_get_device_address + libusb_get_device_descriptor + libusb_get_device_descriptor@8 = libusb_get_device_descriptor + libusb_get_device_list + libusb_get_device_list@8 = libusb_get_device_list + libusb_get_device_speed + libusb_get_device_speed@4 = libusb_get_device_speed + libusb_get_device_string + libusb_get_device_string@16 = libusb_get_device_string + libusb_get_interface_association_descriptors + libusb_get_interface_association_descriptors@12 = libusb_get_interface_association_descriptors + libusb_get_max_alt_packet_size + libusb_get_max_alt_packet_size@16 = libusb_get_max_alt_packet_size + libusb_get_max_iso_packet_size + libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size + libusb_get_max_packet_size + libusb_get_max_packet_size@8 = libusb_get_max_packet_size + libusb_get_max_raw_io_transfer_size + libusb_get_max_raw_io_transfer_size@8 = libusb_get_max_raw_io_transfer_size + libusb_get_next_timeout + libusb_get_next_timeout@8 = libusb_get_next_timeout + libusb_get_parent + libusb_get_parent@4 = libusb_get_parent + libusb_get_platform_descriptor + libusb_get_platform_descriptor@12 = libusb_get_platform_descriptor + libusb_get_pollfds + libusb_get_pollfds@4 = libusb_get_pollfds + libusb_get_port_number + libusb_get_port_number@4 = libusb_get_port_number + libusb_get_port_numbers + libusb_get_port_numbers@12 = libusb_get_port_numbers + libusb_get_port_path + libusb_get_port_path@16 = libusb_get_port_path + libusb_get_session_data + libusb_get_session_data@4 = libusb_get_session_data + libusb_get_ss_endpoint_companion_descriptor + libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor + libusb_get_ss_usb_device_capability_descriptor + libusb_get_ss_usb_device_capability_descriptor@12 = libusb_get_ss_usb_device_capability_descriptor + libusb_get_ssplus_usb_device_capability_descriptor + libusb_get_ssplus_usb_device_capability_descriptor@12 = libusb_get_ssplus_usb_device_capability_descriptor + libusb_get_string_descriptor_ascii + libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii + libusb_get_usb_2_0_extension_descriptor + libusb_get_usb_2_0_extension_descriptor@12 = libusb_get_usb_2_0_extension_descriptor + libusb_get_version + libusb_get_version@0 = libusb_get_version + libusb_handle_events + libusb_handle_events@4 = libusb_handle_events + libusb_handle_events_completed + libusb_handle_events_completed@8 = libusb_handle_events_completed + libusb_handle_events_locked + libusb_handle_events_locked@8 = libusb_handle_events_locked + libusb_handle_events_timeout + libusb_handle_events_timeout@8 = libusb_handle_events_timeout + libusb_handle_events_timeout_completed + libusb_handle_events_timeout_completed@12 = libusb_handle_events_timeout_completed + libusb_has_capability + libusb_has_capability@4 = libusb_has_capability + libusb_hotplug_deregister_callback + libusb_hotplug_deregister_callback@8 = libusb_hotplug_deregister_callback + libusb_hotplug_get_user_data + libusb_hotplug_get_user_data@8 = libusb_hotplug_get_user_data + libusb_hotplug_register_callback + libusb_hotplug_register_callback@36 = libusb_hotplug_register_callback + libusb_init + libusb_init@4 = libusb_init + libusb_init_context + libusb_init_context@12 = libusb_init_context + libusb_interrupt_event_handler + libusb_interrupt_event_handler@4 = libusb_interrupt_event_handler + libusb_interrupt_transfer + libusb_interrupt_transfer@24 = libusb_interrupt_transfer + libusb_kernel_driver_active + libusb_kernel_driver_active@8 = libusb_kernel_driver_active + libusb_lock_event_waiters + libusb_lock_event_waiters@4 = libusb_lock_event_waiters + libusb_lock_events + libusb_lock_events@4 = libusb_lock_events + libusb_open + libusb_open@8 = libusb_open + libusb_open_device_with_vid_pid + libusb_open_device_with_vid_pid@12 = libusb_open_device_with_vid_pid + libusb_pollfds_handle_timeouts + libusb_pollfds_handle_timeouts@4 = libusb_pollfds_handle_timeouts + libusb_ref_device + libusb_ref_device@4 = libusb_ref_device + libusb_release_interface + libusb_release_interface@8 = libusb_release_interface + libusb_reset_device + libusb_reset_device@4 = libusb_reset_device + libusb_set_auto_detach_kernel_driver + libusb_set_auto_detach_kernel_driver@8 = libusb_set_auto_detach_kernel_driver + libusb_set_configuration + libusb_set_configuration@8 = libusb_set_configuration + libusb_set_debug + libusb_set_debug@8 = libusb_set_debug + libusb_set_interface_alt_setting + libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting + libusb_set_log_cb + libusb_set_log_cb@12 = libusb_set_log_cb + libusb_set_option + libusb_set_pollfd_notifiers + libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers + libusb_setlocale + libusb_setlocale@4 = libusb_setlocale + libusb_strerror + libusb_strerror@4 = libusb_strerror + libusb_submit_transfer + libusb_submit_transfer@4 = libusb_submit_transfer + libusb_transfer_get_stream_id + libusb_transfer_get_stream_id@4 = libusb_transfer_get_stream_id + libusb_transfer_set_stream_id + libusb_transfer_set_stream_id@8 = libusb_transfer_set_stream_id + libusb_try_lock_events + libusb_try_lock_events@4 = libusb_try_lock_events + libusb_unlock_event_waiters + libusb_unlock_event_waiters@4 = libusb_unlock_event_waiters + libusb_unlock_events + libusb_unlock_events@4 = libusb_unlock_events + libusb_unref_device + libusb_unref_device@4 = libusb_unref_device + libusb_wait_for_event + libusb_wait_for_event@8 = libusb_wait_for_event + libusb_wrap_sys_device + libusb_wrap_sys_device@12 = libusb_wrap_sys_device diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.rc b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.rc new file mode 100644 index 00000000..bcb10fac --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb-1.0.rc @@ -0,0 +1,53 @@ +/* + * For Windows: input this file to the Resource Compiler to produce a binary + * .res file. This is then embedded in the resultant library (like any other + * compilation object). + * The information can then be queried using standard APIs and can also be + * viewed with utilities such as Windows Explorer. + */ +#include "winresrc.h" + +#include "version.h" +#ifndef LIBUSB_VERSIONSTRING +#define LU_STR(s) #s +#define LU_XSTR(s) LU_STR(s) +#define LIBUSB_VERSIONSTRING \ + LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." \ + LU_XSTR(LIBUSB_MICRO) "." LU_XSTR(LIBUSB_NANO) LIBUSB_RC "\0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO + PRODUCTVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "libusb.info\0" + VALUE "FileDescription", "C library for writing portable USB drivers in userspace\0" + VALUE "FileVersion", LIBUSB_VERSIONSTRING + VALUE "InternalName", "libusb\0" + VALUE "LegalCopyright", "See individual source files, GNU LGPL v2.1 or later.\0" + VALUE "LegalTrademarks", "http://www.gnu.org/licenses/lgpl-2.1.html\0" + VALUE "OriginalFilename", "libusb-1.0.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "libusb-1.0\0" + VALUE "ProductVersion", LIBUSB_VERSIONSTRING + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb.h new file mode 100644 index 00000000..c641955f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusb.h @@ -0,0 +1,2471 @@ +/* + * Public libusb header file + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2012 Pete Batard + * Copyright © 2012-2023 Nathan Hjelm + * Copyright © 2014-2020 Chris Dickens + * For more information, please visit: https://libusb.info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#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 +typedef SSIZE_T ssize_t; +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define LIBUSB_FLEXIBLE_ARRAY /* [] - valid C99 code */ +#else +#define LIBUSB_FLEXIBLE_ARRAY 0 /* [0] - non-standard, but usually working code */ +#endif /* __STDC_VERSION__ */ + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) +#include +#if defined(interface) +#undef interface +#endif +#endif /* _WIN32 || __CYGWIN__ */ + +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) +#elif defined(__GNUC__) && (__GNUC__ >= 3) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define LIBUSB_DEPRECATED_FOR(f) __declspec(deprecated("Use " #f " instead")) +#else +#define LIBUSB_DEPRECATED_FOR(f) +#endif /* __GNUC__ */ + +#if defined(__GNUC__) +#define LIBUSB_PACKED __attribute__ ((packed)) +#else +#define LIBUSB_PACKED +#endif /* __GNUC__ */ + +/** \def LIBUSB_CALL + * \ingroup libusb_misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to functions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#define LIBUSB_CALLV WINAPIV +#else +#define LIBUSB_CALL +#define LIBUSB_CALLV +#endif /* _WIN32 || __CYGWIN__ */ + +/** \def LIBUSB_API_VERSION + * \ingroup libusb_misc + * libusb's API version. + * + * Since version 1.0.18, to help with feature detection, libusb defines + * a LIBUSB_API_VERSION macro that gets increased every time there is a + * significant change to the API, such as the introduction of a new call, + * the definition of a new macro/enum member, or any other element that + * libusb applications may want to detect at compilation time. + * + * Between versions 1.0.13 and 1.0.17 (inclusive) the older spelling of + * LIBUSBX_API_VERSION was used. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusb API + * #endif + * \endcode + * + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + * + * The incremental component has changed as follows: + *
    + *
  • libusbx version 1.0.13: LIBUSBX_API_VERSION = 0x01000100 + *
  • libusbx version 1.0.14: LIBUSBX_API_VERSION = 0x010000FF + *
  • libusbx version 1.0.15: LIBUSBX_API_VERSION = 0x01000101 + *
  • libusbx version 1.0.16: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusbx version 1.0.17: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusb version 1.0.18: LIBUSB_API_VERSION = 0x01000102 + *
  • libusb version 1.0.19: LIBUSB_API_VERSION = 0x01000103 + *
  • libusb version 1.0.20: LIBUSB_API_VERSION = 0x01000104 + *
  • libusb version 1.0.21: LIBUSB_API_VERSION = 0x01000105 + *
  • libusb version 1.0.22: LIBUSB_API_VERSION = 0x01000106 + *
  • libusb version 1.0.23: LIBUSB_API_VERSION = 0x01000107 + *
  • libusb version 1.0.24: LIBUSB_API_VERSION = 0x01000108 + *
  • libusb version 1.0.25: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.26: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.27: LIBUSB_API_VERSION = 0x0100010A + *
  • libusb version 1.0.28: LIBUSB_API_VERSION = 0x0100010A + *
  • libusb version 1.0.29: LIBUSB_API_VERSION = 0x0100010B + *
  • libusb version 1.0.30: LIBUSB_API_VERSION = 0x0100010C + *
+ */ +#define LIBUSB_API_VERSION 0x0100010C + +/** \def LIBUSBX_API_VERSION + * \ingroup libusb_misc + * + * This is the older spelling, kept for backwards compatibility of code + * needing to test for older library versions where the newer spelling + * did not exist. */ +#define LIBUSBX_API_VERSION LIBUSB_API_VERSION + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * \ingroup libusb_misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup libusb_misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup libusb_desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0x00, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 0x01, + + /** Communications class */ + LIBUSB_CLASS_COMM = 0x02, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 0x03, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 0x05, + + /** Image class */ + LIBUSB_CLASS_IMAGE = 0x06, + LIBUSB_CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */ + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 0x07, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 0x08, + + /** Hub class */ + LIBUSB_CLASS_HUB = 0x09, + + /** Data class */ + LIBUSB_CLASS_DATA = 0x0a, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Audio & Video */ + LIBUSB_CLASS_AUDIO_VIDEO = 0x10, + + /** Billboard */ + LIBUSB_CLASS_BILLBOARD = 0x11, + + /** Interface class */ + LIBUSB_CLASS_TYPE_C_BRIDGE = 0x12, + + /** Bulk display */ + LIBUSB_CLASS_BULK_DISPLAY_PROTOCOL = 0x13, + + /** MCTP */ + LIBUSB_CLASS_MCTP = 0x14, + + /** I3C */ + LIBUSB_CLASS_I3C = 0x3c, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Miscellaneous class */ + LIBUSB_CLASS_MISCELLANEOUS = 0xef, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup libusb_desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** Interface Association Descriptor. + * See libusb_interface_association_descriptor */ + LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b, + + /** BOS descriptor */ + LIBUSB_DT_BOS = 0x0f, + + /** Device Capability descriptor */ + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 +#define LIBUSB_DT_INTERFACE_ASSOCIATION_SIZE 8 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE 12 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 +#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE \ + (LIBUSB_DT_BOS_SIZE + \ + LIBUSB_BT_USB_2_0_EXTENSION_SIZE + \ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE + \ + LIBUSB_BT_CONTAINER_ID_SIZE) + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup libusb_desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00, + + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup libusb_desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_endpoint_transfer_type { + /** Control endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0, + + /** Isochronous endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1, + + /** Bulk endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2, + + /** Interrupt endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3 +}; + +/** \ingroup libusb_misc + * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0a, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0b, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0c, + + /** Sets both the U1 and U2 Exit Latency */ + LIBUSB_REQUEST_SET_SEL = 0x30, + + /** Delay from the time a host transmits a packet to the time it is + * received by the device. */ + LIBUSB_SET_ISOCH_DELAY = 0x31 +}; + +/** \ingroup libusb_misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup libusb_misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03 +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0c + +/** \ingroup libusb_desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0x0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 0x1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 0x2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 0x3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup libusb_desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0x0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 0x1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 0x2 +}; + +/** \ingroup libusb_desc + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = (1 << 0), + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = (1 << 1), + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = (1 << 2), + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = (1 << 3) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 0x01, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 0x02, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 0x04, + + /** Platform descriptor */ + LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05, + + /** SuperSpeedPlus device capability */ + LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY = 0x0A, +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.6 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used + * for isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface association descriptor. + * This descriptor is documented in section 9.6.4 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_association_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION + * LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */ + uint8_t bDescriptorType; + + /** Interface number of the first interface that is associated + * with this function */ + uint8_t bFirstInterface; + + /** Number of contiguous interfaces that are associated with + * this function */ + uint8_t bInterfaceCount; + + /** USB-IF class code for this function. + * A value of zero is not allowed in this descriptor. + * If this field is 0xff, the function class is vendor-specific. + * All other values are reserved for assignment by the USB-IF. + */ + uint8_t bFunctionClass; + + /** USB-IF subclass code for this function. + * If this field is not set to 0xff, all values are reserved + * for assignment by the USB-IF + */ + uint8_t bFunctionSubClass; + + /** USB-IF protocol code for this function. + * These codes are qualified by the values of the bFunctionClass + * and bFunctionSubClass fields. + */ + uint8_t bFunctionProtocol; + + /** Index of string descriptor describing this function */ + uint8_t iFunction; +}; + +/** \ingroup libusb_desc + * Structure containing an array of 0 or more interface association + * descriptors + */ +struct libusb_interface_association_descriptor_array { + /** Array of interface association descriptors. The size of this array + * is determined by the length field. + */ + const struct libusb_interface_association_descriptor *iad; + + /** Number of interface association descriptors contained. Read-only. */ + int length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface. + * Must be non-negative. */ + int num_altsetting; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully operation. Expressed in units + * of 2 mA when the device is operating in high-speed mode and in units + * of 8 mA when the device is operating in super-speed mode. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + /** The maximum number of packets the endpoint can send or + * receive as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. Valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup libusb_desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Device Capability type */ + uint8_t bDevCapabilityType; + + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data[LIBUSB_FLEXIBLE_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability[LIBUSB_FLEXIBLE_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_bos_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_bos_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup libusb_desc + * enum used in \ref libusb_ssplus_sublink_attribute + */ +enum libusb_superspeedplus_sublink_attribute_sublink_type { + LIBUSB_SSPLUS_ATTR_TYPE_SYM = 0, + LIBUSB_SSPLUS_ATTR_TYPE_ASYM = 1, +}; + +/** \ingroup libusb_desc + * enum used in \ref libusb_ssplus_sublink_attribute + */ +enum libusb_superspeedplus_sublink_attribute_sublink_direction { + LIBUSB_SSPLUS_ATTR_DIR_RX = 0, + LIBUSB_SSPLUS_ATTR_DIR_TX = 1, +}; + +/** \ingroup libusb_desc + * enum used in \ref libusb_ssplus_sublink_attribute + * Bit = Bits per second + * Kb = Kbps + * Mb = Mbps + * Gb = Gbps + */ +enum libusb_superspeedplus_sublink_attribute_exponent { + LIBUSB_SSPLUS_ATTR_EXP_BPS = 0, + LIBUSB_SSPLUS_ATTR_EXP_KBS = 1, + LIBUSB_SSPLUS_ATTR_EXP_MBS = 2, + LIBUSB_SSPLUS_ATTR_EXP_GBS = 3, +}; + +/** \ingroup libusb_desc + * enum used in \ref libusb_ssplus_sublink_attribute + */ +enum libusb_superspeedplus_sublink_attribute_link_protocol { + LIBUSB_SSPLUS_ATTR_PROT_SS = 0, + LIBUSB_SSPLUS_ATTR_PROT_SSPLUS = 1, +}; + +/** \ingroup libusb_desc + * Expose \ref libusb_ssplus_usb_device_capability_descriptor.sublinkSpeedAttributes + */ +struct libusb_ssplus_sublink_attribute { + /** Sublink Speed Attribute ID (SSID). + This field is an ID that uniquely identifies the speed of this sublink */ + uint8_t ssid; + + /** This field defines the + base 10 exponent times 3, that shall be applied to the + mantissa. */ + enum libusb_superspeedplus_sublink_attribute_exponent exponent; + + /** This field identifies whether the + Sublink Speed Attribute defines a symmetric or + asymmetric bit rate.*/ + enum libusb_superspeedplus_sublink_attribute_sublink_type type; + + /** This field indicates if this + Sublink Speed Attribute defines the receive or + transmit bit rate. */ + enum libusb_superspeedplus_sublink_attribute_sublink_direction direction; + + /** This field identifies the protocol + supported by the link. */ + enum libusb_superspeedplus_sublink_attribute_link_protocol protocol; + + /** This field defines the mantissa that shall be applied to the exponent when + calculating the maximum bit rate. */ + uint16_t mantissa; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeedPlus descriptor + * This descriptor is documented in section 9.6.2.5 of the USB 3.1 specification. + */ +struct libusb_ssplus_usb_device_capability_descriptor { + /** Sublink Speed Attribute Count */ + uint8_t numSublinkSpeedAttributes; + + /** Sublink Speed ID Count */ + uint8_t numSublinkSpeedIDs; + + /** Unique ID to indicates the minimum lane speed */ + uint8_t ssid; + + /** This field indicates the minimum receive lane count.*/ + uint8_t minRxLaneCount; + + /** This field indicates the minimum transmit lane count*/ + uint8_t minTxLaneCount; + + /** Array size is \ref libusb_ssplus_usb_device_capability_descriptor.numSublinkSpeedAttributes */ + struct libusb_ssplus_sublink_attribute sublinkSpeedAttributes[]; +}; + +/** \ingroup libusb_desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_bos_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + +/** \ingroup libusb_desc + * A structure representing a Platform descriptor. + * This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification. + */ +struct libusb_platform_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_bos_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t PlatformCapabilityUUID[16]; + + /** Capability data (bLength - 20) */ + uint8_t CapabilityData[LIBUSB_FLEXIBLE_ARRAY]; +}; + +/** \ingroup libusb_asyncio + * Setup packet for control transfers. */ +#if defined(_MSC_VER) || defined(__WATCOMC__) +#pragma pack(push, 1) +#endif +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +} LIBUSB_PACKED; +#if defined(_MSC_VER) || defined(__WATCOMC__) +#pragma pack(pop) +#endif + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup libusb_lib + * Structure providing the version of the libusb runtime + */ +struct libusb_version { + /** Library major version. */ + uint16_t major; + + /** Library minor version. */ + uint16_t minor; + + /** Library micro version. */ + uint16_t micro; + + /** Library nano version. */ + uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** For ABI compatibility only. */ + const char *describe; +}; + +/** \ingroup libusb_lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_option() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init_context() 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, 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. + */ +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() 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(). + * + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup libusb_dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup libusb_dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, + + /** The device is operating at super speed plus (10000MBit/s). */ + LIBUSB_SPEED_SUPER_PLUS = 5, + + /** The device is operating at super speed plus x2 (20000MBit/s). */ + LIBUSB_SPEED_SUPER_PLUS_X2 = 6, +}; + +/** \ingroup libusb_misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the + message strings in strerror.c when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99 +}; + +/* Total number of error codes in enum libusb_error */ +#define LIBUSB_ERROR_COUNT 14 + +/** \ingroup libusb_asyncio + * Transfer type */ +enum libusb_transfer_type { + /** Control transfer */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0U, + + /** Isochronous transfer */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1U, + + /** Bulk transfer */ + LIBUSB_TRANSFER_TYPE_BULK = 2U, + + /** Interrupt transfer */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3U, + + /** Bulk stream transfer */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4U +}; + +/** \ingroup libusb_asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ +}; + +/** \ingroup libusb_asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = (1U << 0), + + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ + LIBUSB_TRANSFER_FREE_BUFFER = (1U << 1), + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = (1U << 2), + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * \ref LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this + * flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = (1U << 3) +}; + +/** \ingroup libusb_asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup libusb_asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref libusb_asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the transfer from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in milliseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer. Must be non-negative. */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data. Useful for associating specific data to a transfer + * that can be accessed from within the callback function. + * + * This field may be set manually or is taken as the `user_data` parameter + * of the following functions: + * - libusb_fill_bulk_transfer() + * - libusb_fill_bulk_stream_transfer() + * - libusb_fill_control_transfer() + * - libusb_fill_interrupt_transfer() + * - libusb_fill_iso_transfer() */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. Must be non-negative. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc[LIBUSB_FLEXIBLE_ARRAY]; +}; + +/** \ingroup libusb_misc + * Capabilities supported by an instance of libusb on the current running + * platform. Test if the loaded library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0x0000U, + + /** Hotplug support is available on this platform. */ + LIBUSB_CAP_HAS_HOTPLUG = 0x0001U, + + /** The library can access HID devices without requiring user intervention. + * Note that before being able to actually access an HID device, you may + * still have to call additional libusb functions such as + * \ref libusb_detach_kernel_driver(). */ + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100U, + + /** The library supports detaching of the default USB driver, using + * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101U +}; + +/** \ingroup libusb_lib + * Log message levels. + */ +enum libusb_log_level { + /** (0) : No messages ever emitted by the library (default) */ + LIBUSB_LOG_LEVEL_NONE = 0, + + /** (1) : Error messages are emitted */ + LIBUSB_LOG_LEVEL_ERROR = 1, + + /** (2) : Warning and error messages are emitted */ + LIBUSB_LOG_LEVEL_WARNING = 2, + + /** (3) : Informational, warning and error messages are emitted */ + LIBUSB_LOG_LEVEL_INFO = 3, + + /** (4) : All messages are emitted */ + LIBUSB_LOG_LEVEL_DEBUG = 4 +}; + +/** \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 { + /** Callback function handling all log messages. */ + LIBUSB_LOG_CB_GLOBAL = (1 << 0), + + /** Callback function handling context related log messages. */ + LIBUSB_LOG_CB_CONTEXT = (1 << 1) +}; + +/** \ingroup libusb_lib + * Available option values for libusb_set_option() and libusb_init_context(). + */ +enum libusb_option { + /** Set the log message verbosity. + * + * This option must be provided an argument of type \ref libusb_log_level. + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stderr file descriptor. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this option does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this option does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this option + * does nothing: you'll always get messages from all levels. + */ + LIBUSB_OPTION_LOG_LEVEL = 0, + + /** Use the UsbDk backend for a specific context, if available. + * + * This option should be set at initialization with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Windows. Ignored on all other platforms. + */ + LIBUSB_OPTION_USE_USBDK = 1, + + /** Do not scan for devices + * + * With this option set, libusb will skip scanning devices in + * libusb_init_context(). + * + * Hotplug functionality will also be deactivated. + * + * 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. + * + * This option should only be used with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Linux. Ignored on all other platforms. + */ + LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, + +#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY + + /** Set the context log callback function. + * + * Set the log callback function either on a context or globally. This + * option must be provided an argument of type \ref libusb_log_cb. + * Using this option with a NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_GLOBAL. + * Using it with a non-NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_CONTEXT. + */ + LIBUSB_OPTION_LOG_CB = 3, + + LIBUSB_OPTION_MAX = 4 +}; + +/** \ingroup libusb_desc + * The device string type. + */ +enum libusb_device_string_type { + LIBUSB_DEVICE_STRING_MANUFACTURER, + LIBUSB_DEVICE_STRING_PRODUCT, + LIBUSB_DEVICE_STRING_SERIAL_NUMBER, + LIBUSB_DEVICE_STRING_COUNT /* The total number of string types. */ +}; + +/** \ingroup libusb_desc + * The maximum length for a device string descriptor in UTF-8. + * + * 255 max descriptor length with 2 byte header + * => 253 bytes UTF-16LE, no null termination (USB 2.0 9.6.7) + * => 126.5 codepoints + * => 127 * 3 + 1 + * => 382 bytes + * + * Stay with 256 * 3/2 = 384 to be safe. + */ +#define LIBUSB_DEVICE_STRING_BYTES_MAX (384U) + +/** \ingroup libusb_lib + * Callback function for handling log messages. + * \param ctx the context which is related to the log message, or NULL if it + * 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, + enum libusb_log_level level, const char *str); + +/** \ingroup libusb_lib + * Structure used for setting options through \ref libusb_init_context. + * + */ +struct libusb_init_option { + /** Which option to set */ + enum libusb_option option; + /** An integer value used by the option (if applicable). */ + union { + int ival; + libusb_log_cb log_cbval; + } value; +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +int LIBUSB_CALL libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +/* may be deprecated in the future in favor of lubusb_init_context()+libusb_set_option() */ +void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int error_code); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(int errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_device_string(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length); +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( + libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos); +void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); +void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); +int LIBUSB_CALL libusb_get_ssplus_usb_device_capability_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ssplus_usb_device_capability_descriptor **ssplus_usb_device_cap); +void LIBUSB_CALL libusb_free_ssplus_usb_device_capability_descriptor( + struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap); +int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id); +void LIBUSB_CALL libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id); +int LIBUSB_CALL libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor); +void LIBUSB_CALL libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor); +unsigned long LIBUSB_CALL libusb_get_session_data(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len); +LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint); + +int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array); +int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array); +void LIBUSB_CALL libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array); + +int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable); + +int LIBUSB_CALL libusb_endpoint_supports_raw_io(libusb_device_handle* dev_handle, + uint8_t endpoint); +int LIBUSB_CALL libusb_endpoint_set_raw_io(libusb_device_handle *dev_handle, + uint8_t endpoint, int enable); +int LIBUSB_CALL libusb_get_max_raw_io_transfer_size( + libusb_device_handle *dev_handle, + uint8_t endpoint); + +/* async I/O */ + +/** \ingroup libusb_asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup libusb_asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *)(void *)transfer->buffer; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( + struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer using bulk streams. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param stream_id bulk stream id for this transfer + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_stream_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, uint32_t stream_id, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout); + +/** \ingroup libusb_desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev_handle a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); +} + +/** \ingroup libusb_desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup libusb_poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup libusb_poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup libusb_poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +/** \ingroup libusb_hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregistered callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * For more information, see \ref libusb_hotplug. + */ +typedef int libusb_hotplug_callback_handle; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug events */ +typedef enum { + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0), + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1) +} libusb_hotplug_event; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug flags */ +typedef enum { + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = (1 << 0) +} libusb_hotplug_flag; + +/** \ingroup libusb_hotplug + * Convenience macro when not using any flags */ +#define LIBUSB_HOTPLUG_NO_FLAGS 0 + +/** \ingroup libusb_hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup libusb_hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * libusb will call this function later, when a matching event had happened on + * a matching device. See \ref libusb_hotplug for more information. + * + * It is safe to call either libusb_hotplug_register_callback() or + * libusb_hotplug_deregister_callback() from within a callback function. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param ctx context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, libusb_hotplug_event event, void *user_data); + +/** \ingroup libusb_hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be + * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices + * already plugged into the machine. Note that libusb modifies its internal + * device list from a separate thread, while calling hotplug callbacks from + * libusb_handle_events(), so it is possible for a device to already be present + * on, or removed from, its internal device list, while the hotplug callbacks + * still need to be dispatched. This means that when using \ref + * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival + * of the same device, once from libusb_hotplug_register_callback() and once + * from libusb_handle_events(); and/or your callback may be called for the + * removal of a device for which an arrived call was never made. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context to register this callback with + * \param[in] events bitwise or of hotplug events that will trigger this callback. + * See \ref libusb_hotplug_event + * \param[in] flags bitwise or of hotplug flags that affect registration. + * See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) + * \returns \ref LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL 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); + +/** \ingroup libusb_hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to deregister + */ +void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +/** \ingroup libusb_hotplug + * Gets the user_data associated with a hotplug callback. + * + * Since version v1.0.24 \ref LIBUSB_API_VERSION >= 0x01000108 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to get the user_data of + */ +void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +int LIBUSB_CALLV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusbi.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusbi.h new file mode 100644 index 00000000..0fcbdd20 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/libusbi.h @@ -0,0 +1,1599 @@ +/* + * Internal header for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2019 Nathan Hjelm + * Copyright © 2019-2020 Google LLC. All rights reserved. + * Copyright © 2020 Chris Dickens + * + * 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 LIBUSBI_H +#define LIBUSBI_H + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include "libusb.h" + +/* Not all C standard library headers define static_assert in assert.h + * Additionally, Visual Studio treats static_assert as a keyword. + */ +#if !defined(__cplusplus) && !defined(static_assert) && !defined(_MSC_VER) +#define static_assert(cond, msg) _Static_assert(cond, msg) +#endif + +#ifdef NDEBUG +#define ASSERT_EQ(expression, value) (void)expression +#define ASSERT_NE(expression, value) (void)expression +#else +#define ASSERT_EQ(expression, value) assert(expression == value) +#define ASSERT_NE(expression, value) assert(expression != value) +#endif + +#define container_of(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member))) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +#ifndef CLAMP +#define CLAMP(val, min, max) \ + ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* The following is used to silence warnings for unused variables */ +#if defined(UNREFERENCED_PARAMETER) && !defined(__GNUC__) +#define UNUSED(var) UNREFERENCED_PARAMETER(var) +#else +#define UNUSED(var) do { (void)(var); } while(0) +#endif + +/* Macro to align a value up to the next multiple of the size of a pointer */ +#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 +#if defined(__HAIKU__) && defined(__GNUC__) && !defined(__clang__) +/* The Haiku port of libusb has some C++ files and GCC does not define + * anything in stdatomic.h when compiled in C++11 (only in C++23). + * This appears to be a bug in gcc's stdatomic.h, and should be fixed either + * in gcc or in Haiku. Until then, use the gcc builtins. */ +typedef long usbi_atomic_t; +#define usbi_atomic_load(a) __atomic_load_n((a), __ATOMIC_SEQ_CST) +#define usbi_atomic_store(a, v) __atomic_store_n((a), (v), __ATOMIC_SEQ_CST) +#define usbi_atomic_inc(a) __atomic_add_fetch((a), 1, __ATOMIC_SEQ_CST) +#define usbi_atomic_dec(a) __atomic_sub_fetch((a), 1, __ATOMIC_SEQ_CST) +#else +#include +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 +#endif + +/* Internal abstractions for event handling and thread synchronization */ +#if defined(PLATFORM_POSIX) +#include "os/events_posix.h" +#include "os/threads_posix.h" +#elif defined(PLATFORM_WINDOWS) +#include "os/events_windows.h" +#include "os/threads_windows.h" +#endif + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY +#define API_EXPORTEDV LIBUSB_CALLV DEFAULT_VISIBILITY + +#ifdef __cplusplus +extern "C" { +#endif + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +/* Backend specific capabilities */ +#define USBI_CAP_HAS_HID_ACCESS 0x00010000 +#define USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER 0x00020000 + +/* Maximum number of bytes in a log line */ +#define USBI_MAX_LOG_LEN 1024 +/* Terminator for log lines */ +#define USBI_LOG_LINE_END "\n" + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_next_entry(ptr, type, member) \ + list_entry((ptr)->member.next, type, member) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_first_entry(head, type, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_first_entry(head, type, member), \ + n = list_next_entry(pos, type, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, type, member)) + +/* Helper macros to iterate over a list. The structure pointed + * to by "pos" must have a list_head member named "list". + */ +#define for_each_helper(pos, head, type) \ + list_for_each_entry(pos, head, list, type) + +#define for_each_safe_helper(pos, n, head, type) \ + list_for_each_entry_safe(pos, n, head, list, type) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = entry->prev = NULL; +} + +static inline void list_cut(struct list_head *list, struct list_head *head) +{ + if (list_empty(head)) { + list_init(list); + return; + } + + list->next = head->next; + list->next->prev = list; + list->prev = head->prev; + list->prev->next = list; + + list_init(head); +} + +static inline void list_splice_front(struct list_head *list, struct list_head *head) +{ + list->next->prev = head; + list->prev->next = head->next; + head->next->prev = list->prev; + head->next = list->next; +} + +static inline void *usbi_reallocf(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + + if (!ret) + free(ptr); + return ret; +} + +#if !defined(USEC_PER_SEC) +#define USEC_PER_SEC 1000000L +#endif + +#if !defined(NSEC_PER_SEC) +#define NSEC_PER_SEC 1000000000L +#endif + +#define TIMEVAL_IS_VALID(tv) \ + ((tv)->tv_sec >= 0 && \ + (tv)->tv_usec >= 0 && (tv)->tv_usec < USEC_PER_SEC) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec || (ts)->tv_nsec) +#define TIMESPEC_CLEAR(ts) (ts)->tv_sec = (ts)->tv_nsec = 0 +#define TIMESPEC_CMP(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) \ + ? ((a)->tv_nsec CMP (b)->tv_nsec) \ + : ((a)->tv_sec CMP (b)->tv_sec)) +#define TIMESPEC_SUB(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0L) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += NSEC_PER_SEC; \ + } \ + } while (0) + +#if defined(PLATFORM_WINDOWS) +#define TIMEVAL_TV_SEC_TYPE long +#else +#define TIMEVAL_TV_SEC_TYPE time_t +#endif + +/* Some platforms don't have this define */ +#ifndef TIMESPEC_TO_TIMEVAL +#define TIMESPEC_TO_TIMEVAL(tv, ts) \ + do { \ + (tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000L; \ + } while (0) +#endif + +#ifdef ENABLE_LOGGING + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#include +#define snprintf usbi_snprintf +#define vsnprintf usbi_vsnprintf +int usbi_snprintf(char *dst, size_t size, const char *format, ...); +int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list args); +#define LIBUSB_PRINTF_WIN32 +#endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */ + +void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, ...) PRINTF_FORMAT(4, 5); + +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __func__, __VA_ARGS__) + +#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(ctx ,...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) + +#else /* ENABLE_LOGGING */ + +#define usbi_err(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_warn(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_info(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_dbg(ctx, ...) do { (void)(ctx); } while(0) + +#endif /* ENABLE_LOGGING */ + +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) +#define ITRANSFER_CTX(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)) +#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) +#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) + +struct libusb_context { +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + enum libusb_log_level debug; + int debug_fixed; + libusb_log_cb log_handler; +#endif + + /* used for signalling occurrence of an internal event. */ + usbi_event_t event; + +#ifdef HAVE_OS_TIMER + /* used for timeout handling, if supported by OS. + * this timer is maintained to trigger on the next pending timeout */ + usbi_timer_t timer; +#endif + + usbi_mutex_t usb_devs_lock; + struct list_head usb_devs; + + /* A list of open handles. Backends are free to traverse this if required. + */ + usbi_mutex_t open_devs_lock; + struct list_head open_devs; + + /* A list of registered hotplug callbacks */ + usbi_mutex_t hotplug_cbs_lock; + struct list_head hotplug_cbs; + libusb_hotplug_callback_handle next_hotplug_cb_handle; + + /* A flag to indicate that the context is ready for hotplug notifications */ + usbi_atomic_t hotplug_ready; + + /* Note paths taking both this and usbi_transfer->lock must always + * take this lock first */ + usbi_mutex_t flying_transfers_lock; + /* 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 + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + +#if !defined(PLATFORM_WINDOWS) + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; +#endif + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* A thread-local storage key to track which thread is performing event + * handling */ + usbi_tls_key_t event_handling_key; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + + /* A lock to protect internal context event data. */ + usbi_mutex_t event_data_lock; + + /* A bitmask of flags that are set to indicate specific events that need to + * be handled. Protected by event_data_lock. */ + unsigned int event_flags; + + /* A counter that is set when we want to interrupt and prevent event handling, + * in order to safely close a device. Protected by event_data_lock. */ + unsigned int device_close; + + /* A list of currently active event sources. Protected by event_data_lock. */ + struct list_head event_sources; + + /* A list of event sources that have been removed since the last time + * event sources were waited on. Protected by event_data_lock. */ + struct list_head removed_event_sources; + + /* A pointer and count to platform-specific data used for monitoring event + * sources. Only accessed during event handling. */ + void *event_data; + unsigned int event_data_cnt; + + /* A list of pending hotplug messages. Protected by event_data_lock. */ + struct list_head hotplug_msgs; + + /* A list of pending completed transfers. Protected by event_data_lock. */ + struct list_head completed_transfers; + + struct list_head list; +}; + +extern struct libusb_context *usbi_default_context; +extern struct libusb_context *usbi_fallback_context; + +extern usbi_mutex_static_t active_contexts_lock; +extern struct list_head active_contexts_list; + +static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) +{ + 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 { + /* The list of event sources has been modified */ + USBI_EVENT_EVENT_SOURCES_MODIFIED = 1U << 0, + + /* The user has interrupted the event handler */ + USBI_EVENT_USER_INTERRUPT = 1U << 1, + + /* A hotplug callback deregistration is pending */ + USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1U << 2, + + /* One or more hotplug messages are pending */ + USBI_EVENT_HOTPLUG_MSG_PENDING = 1U << 3, + + /* One or more completed transfers are pending */ + USBI_EVENT_TRANSFER_COMPLETED = 1U << 4, + + /* A device is in the process of being closed */ + USBI_EVENT_DEVICE_CLOSE = 1U << 5, +}; + +/* Macros for managing event handling state */ +static inline int usbi_handling_events(struct libusb_context *ctx) +{ + return usbi_tls_key_get(ctx->event_handling_key) != NULL; +} + +static inline void usbi_start_event_handling(struct libusb_context *ctx) +{ + usbi_tls_key_set(ctx->event_handling_key, ctx); +} + +static inline void usbi_end_event_handling(struct libusb_context *ctx) +{ + usbi_tls_key_set(ctx->event_handling_key, NULL); +} + +struct libusb_device { + usbi_atomic_t refcnt; + + struct libusb_context *ctx; + struct libusb_device *parent_dev; + + uint8_t bus_number; + uint8_t port_number; + uint8_t device_address; + enum libusb_speed speed; + + struct list_head list; + unsigned long session_data; + + struct libusb_device_descriptor device_descriptor; + usbi_atomic_t attached; + + char * device_strings_utf8[LIBUSB_DEVICE_STRING_COUNT]; +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + int auto_detach_kernel_driver; +}; + +/* Function called by backend during device initialization to convert + * multi-byte fields in the device descriptor to host-endian format. + */ +static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc) +{ + desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB); + desc->idVendor = libusb_le16_to_cpu(desc->idVendor); + desc->idProduct = libusb_le16_to_cpu(desc->idProduct); + desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice); +} + +#if defined(HAVE_CLOCK_GETTIME) && !defined(__APPLE__) +static inline void usbi_get_monotonic_time(struct timespec *tp) +{ + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0); +} +static inline void usbi_get_real_time(struct timespec *tp) +{ + ASSERT_EQ(clock_gettime(CLOCK_REALTIME, tp), 0); +} +#else +/* If the platform doesn't provide the clock_gettime() function, the backend + * must provide its own clock implementations. Two clock functions are + * required: + * + * usbi_get_monotonic_time(): returns the time since an unspecified starting + * point (usually boot) that is monotonically + * increasing. + * usbi_get_real_time(): returns the time since system epoch. + */ +void usbi_get_monotonic_time(struct timespec *tp); +void usbi_get_real_time(struct timespec *tp); +#endif + +/* in-memory transfer layout: + * + * 1. os private data + * 2. struct usbi_transfer + * 3. struct libusb_transfer (which includes iso packets) [variable size] + * + * You can convert between them with the macros: + * TRANSFER_PRIV_TO_USBI_TRANSFER + * USBI_TRANSFER_TO_TRANSFER_PRIV + * USBI_TRANSFER_TO_LIBUSB_TRANSFER + * LIBUSB_TRANSFER_TO_USBI_TRANSFER + */ + +struct usbi_transfer { + /* 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 + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). + * Note paths taking both this and the flying_transfers_lock must + * always take the flying_transfers_lock first */ + usbi_mutex_t lock; + + struct list_head list; + struct list_head completed_list; + struct timespec timeout; + int transferred; + uint32_t stream_id; + uint32_t state_flags; /* Protected by usbi_transfer->lock */ + uint32_t timeout_flags; /* Protected by the flying_transfers_lock */ + + /* The device reference is held until destruction for logging + * even after dev_handle is set to NULL. */ + struct libusb_device *dev; + + void *priv; +}; + +enum usbi_transfer_state_flags { + /* Transfer successfully submitted by backend */ + USBI_TRANSFER_IN_FLIGHT = 1U << 0, + + /* Cancellation was requested via libusb_cancel_transfer() */ + USBI_TRANSFER_CANCELLING = 1U << 1, + + /* Operation on the transfer failed because the device disappeared */ + USBI_TRANSFER_DEVICE_DISAPPEARED = 1U << 2, +}; + +enum usbi_transfer_timeout_flags { + /* Set by backend submit_transfer() if the OS handles timeout */ + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1U << 0, + + /* The transfer timeout has been handled */ + USBI_TRANSFER_TIMEOUT_HANDLED = 1U << 1, + + /* The transfer timeout was successfully processed */ + USBI_TRANSFER_TIMED_OUT = 1U << 2, +}; + +#define TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv) \ + ((struct usbi_transfer *) \ + ((unsigned char *)(transfer_priv) \ + + PTR_ALIGN(sizeof(*transfer_priv)))) + +#define USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer) \ + ((unsigned char *) \ + ((unsigned char *)(itransfer) \ + - PTR_ALIGN(usbi_backend.transfer_priv_size))) + +#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \ + ((struct libusb_transfer *) \ + ((unsigned char *)(itransfer) \ + + PTR_ALIGN(sizeof(struct usbi_transfer)))) + +#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *) \ + ((unsigned char *)(transfer) \ + - PTR_ALIGN(sizeof(struct usbi_transfer)))) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +/* All standard descriptors have these 2 fields in common */ +struct usbi_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +} LIBUSB_PACKED; + +struct usbi_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} LIBUSB_PACKED; + +struct usbi_configuration_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} LIBUSB_PACKED; + +struct usbi_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} LIBUSB_PACKED; + +struct usbi_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[LIBUSB_FLEXIBLE_ARRAY]; +} LIBUSB_PACKED; + +struct usbi_bos_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} LIBUSB_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +union usbi_config_desc_buf { + struct usbi_configuration_descriptor desc; + uint8_t buf[LIBUSB_DT_CONFIG_SIZE]; + uint16_t align; /* Force 2-byte alignment */ +}; + +union usbi_string_desc_buf { + struct usbi_string_descriptor desc; + uint8_t buf[255]; /* Some devices choke on size > 255 */ + uint16_t align; /* Force 2-byte alignment */ +}; + +union usbi_bos_desc_buf { + struct usbi_bos_descriptor desc; + uint8_t buf[LIBUSB_DT_BOS_SIZE]; + 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); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_context *ctx, struct libusb_device_handle *dev_handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer); +void usbi_signal_transfer_completion(struct usbi_transfer *itransfer); + +void usbi_attach_device(struct libusb_device *dev); +void usbi_detach_device(struct libusb_device *dev); + +void usbi_connect_device(struct libusb_device *dev); +void usbi_disconnect_device(struct libusb_device *dev); + +struct usbi_event_source_data { + usbi_os_handle_t os_handle; + short poll_events; +}; + +struct usbi_event_source { + struct usbi_event_source_data data; + struct list_head list; +}; + +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; + libusb_log_cb log_cbval; + } arg; +}; + +/* OS event abstraction */ + +int usbi_create_event(usbi_event_t *event); +void usbi_destroy_event(usbi_event_t *event); +void usbi_signal_event(usbi_event_t *event); +void usbi_clear_event(usbi_event_t *event); + +#ifdef HAVE_OS_TIMER +int usbi_create_timer(usbi_timer_t *timer); +void usbi_destroy_timer(usbi_timer_t *timer); +int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout); +int usbi_disarm_timer(usbi_timer_t *timer); +#endif + +static inline int usbi_using_timer(struct libusb_context *ctx) +{ +#ifdef HAVE_OS_TIMER + return usbi_timer_valid(&ctx->timer); +#else + UNUSED(ctx); + return 0; +#endif +} + +struct usbi_reported_events { + union { + struct { + unsigned int event_triggered:1; +#ifdef HAVE_OS_TIMER + unsigned int timer_triggered:1; +#endif + }; + unsigned int event_bits; + }; + void *event_data; + unsigned int event_data_count; + unsigned int num_ready; +}; + +int usbi_alloc_event_data(struct libusb_context *ctx); +int usbi_wait_for_events(struct libusb_context *ctx, + struct usbi_reported_events *reported_events, int timeout_ms); + +/* accessor functions for structure private data */ + +static inline void *usbi_get_context_priv(struct libusb_context *ctx) +{ + return (unsigned char *)ctx + PTR_ALIGN(sizeof(*ctx)); +} + +static inline void *usbi_get_device_priv(struct libusb_device *dev) +{ + return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev)); +} + +static inline void *usbi_get_device_handle_priv(struct libusb_device_handle *dev_handle) +{ + return (unsigned char *)dev_handle + PTR_ALIGN(sizeof(*dev_handle)); +} + +static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer) +{ + return itransfer->priv; +} + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * 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. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices[LIBUSB_FLEXIBLE_ARRAY]; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Binary mask for backend specific capabilities */ + uint32_t caps; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * 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. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * 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); + + /* Set a backend-specific option. Optional. + * + * This function is called when the user calls libusb_set_option() and + * the option is not handled by the core library. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*set_option)(struct libusb_context *ctx, enum libusb_option option, + va_list args); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a reference + * to a libusb_device structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, call + * usbi_connect_device() to add the device to the context's device list + * and make it discoverable by usbi_get_device_by_session_id(). + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * If the backend has hotplug support, this function is not used! + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Retrieve a device string without needing to open the device. + * + * The string should be retrieved without opening the device + * and ideally without performing USB transactions to the device. + * Most operating systems read and cache the common string + * descriptors. Use the OS-specific calls to retrieve these strings. + * + * Since the USB string descriptor could be processed by the OS, + * this function returns a UTF-8 encoded string. + * + * The string will be returned untranslated or in the default OS language + * when supported by the OS and USB device. + * + * This function must not write more than length bytes into data, + * including the null terminator. + * + * Return: + * - The actual length in bytes including the null termintor on success. + * - LIBUSB_ERROR_NO_DEVICE if device not found. + * - LIBUSB_ERROR_INVALID_PARAM if any parameter is invalid. + * - another LIBUSB_ERROR code on other failure + */ + int (*get_device_string)(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length); + + /* Apps which were written before hotplug support, may listen for + * hotplug events on their own and call libusb_get_device_list on + * device addition. In this case libusb_get_device_list will likely + * return a list without the new device in there, as the hotplug + * event thread will still be busy enumerating the device, which may + * take a while, or may not even have seen the event yet. + * + * To avoid this libusb_get_device_list will call this optional + * function for backends with hotplug support before copying + * ctx->usb_devs to the user. In this function the backend should + * ensure any pending hotplug events are fully processed before + * returning. + * + * Optional, should be implemented by backends with hotplug support. + */ + void (*hotplug_poll)(void); + + /* Wrap a platform-specific device handle for I/O and other USB + * operations. The device handle is preallocated for you. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_event_source() function. + * + * Your backend should also initialize the device structure + * (dev_handle->dev), which is NULL at the beginning of the call. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to wrap an existing + * platform-specific device handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*wrap_sys_device)(struct libusb_context *ctx, + struct libusb_device_handle *dev_handle, intptr_t sys_dev); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_event_source() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *dev_handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_event_source() to + * inform libusb of any event sources associated with this device that + * should no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *dev_handle); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + void *buffer, size_t len); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). + * + * Return the length read on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, void *buffer, size_t len); + + /* Like get_config_descriptor but then by bConfigurationValue instead + * of by index. + * + * Optional, if not present the core will call get_config_descriptor + * for all configs until it finds the desired bConfigurationValue. + * + * Returns a pointer to the raw-descriptor in *buffer, this memory + * is valid as long as device is valid. + * + * Returns the length of the returned raw-descriptor on success, + * or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor_by_value)(struct libusb_device *device, + uint8_t bConfigurationValue, void **buffer); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *dev_handle, uint8_t *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *dev_handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applications can use the interface because + * we now "own" it. + * + * This function gets called with dev_handle->lock locked! + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * This function gets called with dev_handle->lock locked! + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, + uint8_t interface_number, uint8_t altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *dev_handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. Optional. + * + * If possible, the device handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the reset + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *dev_handle); + + /* Alloc num_streams usb3 bulk streams on the passed in endpoints */ + int (*alloc_streams)(struct libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); + + /* Free usb3 bulk streams allocated with alloc_streams */ + int (*free_streams)(struct libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + + /* Allocate persistent DMA memory for the given device, suitable for + * zerocopy. May return NULL on failure. Optional to implement. + */ + void *(*dev_mem_alloc)(struct libusb_device_handle *handle, size_t len); + + /* Free memory allocated by dev_mem_alloc. */ + int (*dev_mem_free)(struct libusb_device_handle *handle, void *buffer, + size_t len); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *dev_handle, + uint8_t interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle, + uint8_t interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle, + uint8_t interface_number); + + /** Check if RAW_IO is supported by an endpoint. + * + * Return: + * - 1 if yes + * - 0 if no + * - a LIBUSB_ERROR code on failure + */ + int (*endpoint_supports_raw_io)(struct libusb_device_handle* dev_handle, + uint8_t endpoint); + + /** Enable/disable RAW_IO for an endpoint. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_SUPPORTED if RAW_IO is not supported by the endpoint + * - another LIBUSB_ERROR code on other failure + */ + int (*endpoint_set_raw_io)(struct libusb_device_handle* dev_handle, + uint8_t endpoint, int enable); + + /* Retrieve the maximum transfer size in bytes supported for WinUSB RAW_IO + * for an inbound bulk or interrupt endpoint on an open device. Optional. + * + * Return: + * - a positive maximum transfer size on success + * - a LIBUSB_ERROR code on failure + */ + int (*get_max_raw_io_transfer_size)( + struct libusb_device_handle *dev_handle, + uint8_t endpoint); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * This function gets called with itransfer->lock locked! + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + * + * This function gets called with itransfer->lock locked! + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events on event sources. Optional. + * + * Provide this function when event sources directly indicate device + * or transfer activity. If your backend does not have such event sources, + * implement the handle_transfer_completion function below. + * + * This involves monitoring any active transfers and processing their + * completion or cancellation. + * + * The function is passed a pointer that represents platform-specific + * data for monitoring event sources (size count). This data is to be + * (re)allocated as necessary when event sources are modified. + * The num_ready parameter indicates the number of event sources that + * have reported events. This should be enough information for you to + * determine which actions need to be taken on the currently active + * transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + void *event_data, unsigned int count, unsigned int num_ready); + + /* Handle transfer completion. Optional. + * + * Provide this function when there are no event sources available that + * directly indicate device or transfer activity. If your backend does + * have such event sources, implement the handle_events function above. + * + * Your backend must tell the library when a transfer has completed by + * calling usbi_signal_transfer_completion(). You should store any private + * information about the transfer and its completion status in the transfer's + * private backend data. + * + * During event handling, this function will be called on each transfer for + * which usbi_signal_transfer_completion() was called. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_transfer_completion)(struct usbi_transfer *itransfer); + + /* Number of bytes to reserve for per-context private backend data. + * This private data area is accessible by calling + * usbi_get_context_priv() on the libusb_context instance. + */ + size_t context_priv_size; + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible by calling + * usbi_get_device_priv() on the libusb_device instance. + */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible by calling + * usbi_get_device_handle_priv() on the libusb_device_handle instance. + */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_get_transfer_priv() on the usbi_transfer instance. + */ + size_t transfer_priv_size; +}; + +extern const struct usbi_os_backend usbi_backend; + +#define for_each_context(c) \ + for_each_helper(c, &active_contexts_list, struct libusb_context) + +#define for_each_device(ctx, d) \ + for_each_helper(d, &(ctx)->usb_devs, struct libusb_device) + +#define for_each_device_safe(ctx, d, n) \ + for_each_safe_helper(d, n, &(ctx)->usb_devs, struct libusb_device) + +#define for_each_open_device(ctx, h) \ + for_each_helper(h, &(ctx)->open_devs, struct libusb_device_handle) + +#define __for_each_transfer(list, t) \ + for_each_helper(t, (list), struct usbi_transfer) + +#define for_each_transfer(ctx, t) \ + __for_each_transfer(&(ctx)->flying_transfers, t) + +#define __for_each_transfer_safe(list, t, n) \ + for_each_safe_helper(t, n, (list), struct usbi_transfer) + +#define for_each_transfer_safe(ctx, t, n) \ + __for_each_transfer_safe(&(ctx)->flying_transfers, t, n) + +#define __for_each_completed_transfer_safe(list, t, n) \ + list_for_each_entry_safe(t, n, (list), completed_list, struct usbi_transfer) + +#define for_each_event_source(ctx, e) \ + for_each_helper(e, &(ctx)->event_sources, struct usbi_event_source) + +#define for_each_removed_event_source(ctx, e) \ + for_each_helper(e, &(ctx)->removed_event_sources, struct usbi_event_source) + +#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 + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.c new file mode 100644 index 00000000..22287c72 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.c @@ -0,0 +1,3057 @@ +/* -*- Mode: C; indent-tabs-mode:nil -*- */ +/* + * darwin backend for libusb 1.0 + * Copyright © 2008-2023 Nathan Hjelm + * Copyright © 2019-2023 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector + * 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 (10ULL * USEC_PER_SEC) + +#include +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + #include +#endif + +#include "darwin_usb.h" + +/* Both kIOMasterPortDefault or kIOMainPortDefault are synonyms for 0. */ +static const mach_port_t darwin_default_master_port = 0; + +/* async event thread */ +/* if both this mutex and darwin_cached_devices_mutex are to be acquired then + darwin_cached_devices_mutex must be acquired first. */ +static usbi_mutex_static_t libusb_darwin_at_mutex = USBI_MUTEX_INITIALIZER; +static usbi_cond_t libusb_darwin_at_cond = USBI_COND_INITIALIZER; + +#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1) + +static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ +static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */ + +static usbi_mutex_t darwin_cached_devices_mutex = USBI_MUTEX_INITIALIZER; +static struct list_head darwin_cached_devices; +static int init_count; + +static const char *darwin_device_class = "IOUSBDevice"; + +uint32_t libusb_testonly_fake_running_version __attribute__ ((visibility ("hidden"))); +uint32_t libusb_testonly_using_running_interface_version __attribute__ ((visibility ("hidden"))); +uint32_t libusb_testonly_using_running_device_version __attribute__ ((visibility ("hidden"))); +bool libusb_testonly_clear_running_version_cache __attribute__ ((visibility ("hidden"))); + +#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev) + +/* async event thread */ +static pthread_t libusb_darwin_at; + +/* protected by libusb_darwin_at_mutex */ +static bool libusb_darwin_at_started; + +static void darwin_exit(struct libusb_context *ctx); +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(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out, + UInt64 *old_session_id); + +struct darwin_iokit_interface { + uint32_t min_os_version; + uint32_t version; + CFUUIDRef interface_id; +}; + +static const struct darwin_iokit_interface *get_interface_interface(void) { + const struct darwin_iokit_interface interfaces[] = { +#if defined(kIOUSBInterfaceInterfaceID800) + { + .min_os_version = 101200, + .version = 800, + .interface_id = kIOUSBInterfaceInterfaceID800, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID700) + { + .min_os_version = 101000, + .version = 700, + .interface_id = kIOUSBInterfaceInterfaceID700, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID650) + { + .min_os_version = 100900, + .version = 650, + .interface_id = kIOUSBInterfaceInterfaceID650 + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID550) + { + .min_os_version = 100803, + .version = 550, + .interface_id = kIOUSBInterfaceInterfaceID550, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID245) + { + .min_os_version = 100407, + .version = 245, + .interface_id = kIOUSBInterfaceInterfaceID245, + }, +#endif + { + .min_os_version = 100000, + .version = 220, + .interface_id = kIOUSBInterfaceInterfaceID220, + }, + { + .version = 0, + }, + }; + static struct darwin_iokit_interface cached_interface = {.version = 0}; + if (libusb_testonly_clear_running_version_cache) { + memset (&cached_interface, 0, sizeof (cached_interface)); + } + if (0 == cached_interface.version) { + uint32_t os_version = get_running_version(); + for (int i = 0 ; interfaces[i].version > 0 ; ++i) { + if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) { + cached_interface = interfaces[i]; + } + } + + libusb_testonly_using_running_interface_version = cached_interface.version; + } + + return &cached_interface; +} + +static CFUUIDRef get_interface_interface_id(void) { + return get_interface_interface()->interface_id; +} + +static uint32_t get_interface_interface_version(void) { + return get_interface_interface()->version; +} + +static const struct darwin_iokit_interface *get_device_interface(void) { + struct darwin_iokit_interface interfaces[] = { +#if defined(kIOUSBDeviceInterfaceID650) + { + .min_os_version = 100900, + .version = 650, + .interface_id = kIOUSBDeviceInterfaceID650, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID500) + { + .min_os_version = 100703, + .version = 500, + .interface_id = kIOUSBDeviceInterfaceID500, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID320) + { + .min_os_version = 100504, + .version = 320, + .interface_id = kIOUSBDeviceInterfaceID320, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID300) + { + .min_os_version = 100500, + .version = 300, + .interface_id = kIOUSBDeviceInterfaceID300, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID245) + { + .min_os_version = 100407, + .version = 245, + .interface_id = kIOUSBDeviceInterfaceID245, + }, +#endif + { + .min_os_version = 100000, + .version = 197, + .interface_id = kIOUSBDeviceInterfaceID197, + }, + { + .version = 0, + }, + }; + static struct darwin_iokit_interface cached_interface = {.version = 0}; + if (libusb_testonly_clear_running_version_cache) { + memset (&cached_interface, 0, sizeof (cached_interface)); + } + if (0 == cached_interface.version) { + uint32_t os_version = get_running_version(); + for (int i = 0 ; interfaces[i].version > 0 ; ++i) { + if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) { + cached_interface = interfaces[i]; + } + } + libusb_testonly_using_running_device_version = cached_interface.version; + } + + return &cached_interface; +} + +static CFUUIDRef get_device_interface_id(void) { + return get_device_interface()->interface_id; +} + +static uint32_t get_device_interface_version(void) { + return get_device_interface()->version; +} + +struct darwin_pipe_properties { + uint8_t number; + uint8_t direction; + uint8_t transfer_type; + uint16_t max_packet_size; // without multipliers, not "full" + uint8_t interval; +}; +typedef struct darwin_pipe_properties darwin_pipe_properties_t; + +static IOReturn darwin_get_pipe_properties(struct darwin_interface *cInterface, uint8_t pipe, darwin_pipe_properties_t *out) { + IOReturn kresult; + +#if (MAX_INTERFACE_VERSION >= 550) + if (get_interface_interface_version() >= 550) { + // GetPipePropertiesV3 returns a "cooked" wMaxPacketSize (premultiplied by burst and mul). This not what we want. + // We only call GetPipePropertiesV3 to fill the fields needed to call GetEndpointPropertiesV3. + IOUSBEndpointProperties pipe_properties = {.bVersion = kUSBEndpointPropertiesVersion3}; + kresult = (*IOINTERFACE_V(cInterface, 550))->GetPipePropertiesV3 (IOINTERFACE(cInterface), pipe, &pipe_properties); + if (kIOReturnSuccess != kresult) { + return kresult; + } + + // GetEndpointPropertiesV3 returns the wMaxPacketSize without burst and mul multipliers. + kresult = (*IOINTERFACE_V(cInterface, 550))->GetEndpointPropertiesV3 (IOINTERFACE(cInterface), &pipe_properties); + if (kIOReturnSuccess == kresult) { + out->number = pipe_properties.bEndpointNumber; + out->direction = pipe_properties.bDirection; + out->transfer_type = pipe_properties.bTransferType; + out->max_packet_size = pipe_properties.wMaxPacketSize; + out->interval = pipe_properties.bInterval; + } + return kresult; + } +#endif + // GetPipeProperties returns a "cooked" version of max_packet_size which includes burst and mul. What we want is the + // original maxPacketSize so we can send zero-length packet when requested by users. + // We only call GetPipeProperties to retrieve the parameters needed to call GetEndpointProperties. + kresult = (*IOINTERFACE(cInterface))->GetPipeProperties(IOINTERFACE(cInterface), pipe, &out->direction, + &out->number, &out->transfer_type, &out->max_packet_size, + &out->interval); + if (kIOReturnSuccess != kresult) { + return kresult; + } + + // To call GetEndpointProperties we also need altSetting + UInt8 altSetting; + kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting(IOINTERFACE(cInterface), &altSetting); + if (kIOReturnSuccess != kresult) { + return kresult; + } + // Retrieve "uncooked" version of maxPacketSize + return (*IOINTERFACE(cInterface))->GetEndpointProperties(IOINTERFACE(cInterface), altSetting, out->number, + out->direction, &out->transfer_type, &out->max_packet_size, + &out->interval); +} + +#if defined(ENABLE_LOGGING) +static const char *darwin_error_str (IOReturn result) { + static char string_buffer[50]; + switch (result) { + case kIOReturnSuccess: + return "no error"; + case kIOReturnNotOpen: + return "device not opened for exclusive access"; + case kIOReturnNoDevice: + return "no connection to an IOService"; + case kIOUSBNoAsyncPortErr: + return "no async port has been opened for interface"; + 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"; + case kIOUSBTransactionTimeout: + return "transaction timed out"; + case kIOReturnBadArgument: + return "invalid argument"; + case kIOReturnAborted: + return "transaction aborted"; + case kIOReturnNotResponding: + return "device not responding"; + case kIOReturnOverrun: + return "data overrun"; + case kIOReturnCannotWire: + return "physical memory can not be wired down"; + case kIOReturnNoResources: + return "out of resources"; + case kIOUSBHighSpeedSplitError: + return "high speed split error"; + case kIOUSBUnknownPipeErr: + return "pipe ref not recognized"; + default: + snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result); + return string_buffer; + } +} +#endif + +static enum libusb_error darwin_to_libusb (IOReturn result) { + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_SUCCESS; + case kIOReturnNotOpen: + case kIOReturnNoDevice: + return LIBUSB_ERROR_NO_DEVICE; + 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: + default: + return LIBUSB_ERROR_OTHER; + } +} + +uint32_t get_running_version(void) { + if (libusb_testonly_fake_running_version > 0) { + return libusb_testonly_fake_running_version; + } + + int ret; +#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 + char os_version_string[64] = {'\0'};; + size_t os_version_string_len = sizeof(os_version_string) - 1; + + /* newer versions of macOS provide a sysctl for the OS version but this is not useful for iOS without + * code detecting this is iOS and a mapping from iOS -> macOS version. it is still useful to have since + * it provides the exact macOS version instead of the approximate version (as below). */ + ret = sysctlbyname("kern.osproductversion", os_version_string, &os_version_string_len, NULL, 0); + if (ret == 0) { + unsigned int major = 10, minor = 0, patch = 0; + ret = sscanf(os_version_string, "%u.%u.%u", &major, &minor, &patch); + if (ret < 2) { + usbi_err (NULL, "could not determine the running OS version, assuming 10.0, kern.osproductversion=%s", os_version_string); + return 10 * 10000; + } + return (major * 10000) + (minor * 100) + patch; + } +#endif + + char os_release_string[64] = {'\0'}; + size_t os_release_string_len = sizeof(os_release_string) - 1; + /* if the version can not be detected libusb assumes 10.0 so ignore any error here */ + ret = sysctlbyname("kern.osrelease", os_release_string, &os_release_string_len, NULL, 0); + if (ret != 0) { + usbi_err (NULL, "could not read kern.osrelease, errno=", errno); + return 10 * 10000; + } + + unsigned int darwin_major = 1, darwin_minor = 0; + ret = sscanf(os_release_string, "%u.%u", &darwin_major, &darwin_minor); + if (ret < 1) { + usbi_err (NULL, "could not determine the running Darwin version, assuming 1.3 (OS X 10.0), kern.osrelease=%s", os_release_string); + return 10 * 10000; + } + + unsigned int major = 10, minor = 0, patch = 0; + + if (1 == darwin_major && darwin_minor < 4) { + /* 10.0.x */ + } else if (darwin_major < 6) { + /* assume 10.1 for anything in this range */ + minor = 1; + } else if (darwin_major < 20) { + /* from macOS 10.2 through 10.15 the minor version can be calculated from the darwin_major by subtracting 4 and + * the patch level almost always matches darwin_minor. when the darwin_minor does not match the OS X patch level + * it is usually because Apple did not change it in a particular point release. when darwin_minor is changed it + * always matches the OS X/macOS patch level. */ + minor = darwin_major - 4; + patch = darwin_minor; + } else { + /* unlikely to be used as kern.osproductversion is available from 10.10 on */ + major = darwin_major - 9; + minor = darwin_minor; + /* ignore the patch level in this range */ + } + + return (major * 10000) + (minor * 100) + patch; +} + +/* this function must be called with the darwin_cached_devices_mutex held */ +static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount--; + /* free the device and remove it from the cache */ + if (0 == cached_dev->refcount) { + list_del(&cached_dev->list); + + if (cached_dev->device) { + (*cached_dev->device)->Release(cached_dev->device); + cached_dev->device = NULL; + } + IOObjectRelease (cached_dev->service); + free (cached_dev); + } +} + +static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount++; +} + +static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + + /* current interface */ + struct darwin_interface *cInterface; + + uint8_t i, iface; + + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + + usbi_dbg (ctx, "converting ep address 0x%02x to pipeRef and interface", ep); + + usbi_mutex_lock(&dev_handle->lock); + for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { + cInterface = &priv->interfaces[iface]; + + if (dev_handle->claimed_interfaces & (1U << iface)) { + for (i = 0 ; i < cInterface->num_endpoints ; i++) { + if (cInterface->endpoint_addrs[i] == ep) { + *pipep = i + 1; + + if (ifcp) + *ifcp = iface; + + if (interface_out) + *interface_out = cInterface; + + usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface); + usbi_mutex_unlock(&dev_handle->lock); + return LIBUSB_SUCCESS; + } + } + } + } + + /* No pipe found with the correct endpoint address */ + usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep); + + usbi_mutex_unlock(&dev_handle->lock); + + return LIBUSB_ERROR_NOT_FOUND; +} + +static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { + CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class); + + if (!matchingDict) + return kIOReturnError; + + if (location) { + CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + /* there are no unsigned CFNumber types so treat the value as signed. the OS seems to do this + internally (CFNumberType of locationID is kCFNumberSInt32Type) */ + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); + + if (propertyMatchDict && locationCF) { + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); + CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + } + /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */ + + /* release our references as per the Create Rule */ + if (propertyMatchDict) + CFRelease (propertyMatchDict); + if (locationCF) + CFRelease (locationCF); + } + + return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator); +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { + CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + Boolean success = 0; + + if (cfNumber) { + if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { + success = CFNumberGetValue(cfNumber, type, p); + } + + CFRelease (cfNumber); + } + + return (success != 0); +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) { + CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + bool success = false; + + if (cfData) { + if (CFGetTypeID (cfData) == CFDataGetTypeID ()) { + CFIndex length = CFDataGetLength (cfData); + if (length < size) { + size = length; + } + + CFDataGetBytes (cfData, CFRangeMake(0, size), p); + success = true; + } + + CFRelease (cfData); + } + + return success; +} + +static int darwin_device_from_service (struct libusb_context *ctx, io_service_t service, usb_device_t* device) +{ + io_cf_plugin_ref_t *plugInInterface = NULL; + IOReturn kresult; + SInt32 score; + + const int max_retries = 5; + + /* The IOCreatePlugInInterfaceForService function might consistently return + an "out of resources" error with certain USB devices the first time we run + it. The reason is still unclear, but retrying fixes the problem */ + for (int count = 0; count < max_retries; count++) { + kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); + if (kIOReturnSuccess == kresult && plugInInterface) { + break; + } + + usbi_dbg (ctx, "set up plugin for service retry: %s", darwin_error_str (kresult)); + + /* sleep for a little while before trying again */ + const struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000}; + nanosleep(&delay, NULL); + } + + if (kIOReturnSuccess != kresult) { + usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult)); + return darwin_to_libusb(kresult); + } + if (!plugInInterface) { + usbi_dbg (ctx, "could not set up plugin for service"); + return LIBUSB_ERROR_OTHER; + } + + (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(get_device_interface_id()), + (LPVOID *)device); + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); + + return LIBUSB_SUCCESS; +} + +static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { + UNUSED(ptr); + struct darwin_cached_device *cached_device; + UInt64 old_session_id; + struct libusb_context *ctx; + io_service_t service; + int ret; + + usbi_mutex_lock(&active_contexts_lock); + + while ((service = IOIteratorNext(add_devices))) { + ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id); + if (ret < 0 || !cached_device->can_enumerate) { + continue; + } + + /* add this device to each active context's device list */ + for_each_context(ctx) { + process_new_device (ctx, cached_device, old_session_id); + } + + if (atomic_exchange(&cached_device->in_reenumerate, false)) { + usbi_dbg (NULL, "cached device in reset state. reset complete..."); + } + + IOObjectRelease(service); + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { + UNUSED(ptr); + struct libusb_device *dev = NULL; + struct libusb_context *ctx; + struct darwin_cached_device *old_device; + + io_service_t device; + + usbi_mutex_lock(&active_contexts_lock); + + while ((device = IOIteratorNext (rem_devices)) != 0) { + bool is_reenumerating = false; + + /* get the location from the i/o registry */ + UInt64 session = 0; + bool ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); + UInt32 locationID = 0; + (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); + IOObjectRelease (device); + if (!ret) + continue; + + /* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function + otherwise no cached device will ever get freed */ + usbi_mutex_lock(&darwin_cached_devices_mutex); + list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) { + if (old_device->session == session) { + if (atomic_load(&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 (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 + ", locationID: 0x%" PRIx32, session, locationID); + + /* the device object is no longer usable so go ahead and release it */ + if (old_device->device) { + (*old_device->device)->Release(old_device->device); + old_device->device = NULL; + } + + is_reenumerating = true; + } else { + darwin_deref_cached_device (old_device); + } + + break; + } + } + + usbi_mutex_unlock(&darwin_cached_devices_mutex); + if (is_reenumerating) { + continue; + } + + for_each_context(ctx) { + usbi_dbg (ctx, "notifying context %p of device disconnect", ctx); + + dev = usbi_get_device_by_session_id(ctx, (unsigned long) session); + if (dev) { + /* signal the core that this device has been disconnected. the core will tear down this device + when the reference count reaches 0 */ + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } + } + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_hotplug_poll (void) +{ + /* not sure if 1 ms will be too long/short but it should work ok */ + mach_timespec_t timeout = {.tv_sec = 0, .tv_nsec = 1000000UL}; + + /* 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 (darwin_default_master_port, &timeout); +} + +static void darwin_clear_iterator (io_iterator_t iter) { + io_service_t device; + + while ((device = IOIteratorNext (iter)) != 0) + IOObjectRelease (device); +} + +static void darwin_fail_startup(void) { + usbi_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfl = LIBUSB_DARWIN_STARTUP_FAILURE; + usbi_cond_signal (&libusb_darwin_at_cond); + usbi_mutex_unlock (&libusb_darwin_at_mutex); + pthread_exit (NULL); +} + +static void *darwin_event_thread_main (void *arg0) { + UNUSED(arg0); + IOReturn kresult; + CFRunLoopRef runloop; + CFRunLoopSourceRef libusb_shutdown_cfsource; + CFRunLoopSourceContext libusb_shutdown_cfsourcectx; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + /* Set this thread's name, so it can be seen in the debugger + and crash reports. */ + pthread_setname_np ("org.libusb.device-hotplug"); +#endif + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + /* Tell the Objective-C garbage collector about this thread. + This is required because, unlike NSThreads, pthreads are + not automatically registered. Although we don't use + Objective-C, we use CoreFoundation, which does. + Garbage collection support was entirely removed in 10.12, + so don't bother there. */ + objc_registerThreadWithCollector(); +#endif + + /* hotplug (device arrival/removal) sources */ + CFRunLoopSourceRef libusb_notification_cfsource; + io_notification_port_t libusb_notification_port; + io_iterator_t libusb_rem_device_iterator; + io_iterator_t libusb_add_device_iterator; + + /* ctx must only be used for logging during thread startup */ + usbi_dbg (NULL, "creating hotplug event source"); + + runloop = CFRunLoopGetCurrent (); + CFRetain (runloop); + + /* add the shutdown cfsource to the run loop */ + memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx)); + libusb_shutdown_cfsourcectx.info = runloop; + libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop; + libusb_shutdown_cfsource = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx); + CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); + + /* add the notification port to the run loop */ + libusb_notification_port = IONotificationPortCreate (darwin_default_master_port); + libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port); + CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* create notifications for removed devices */ + kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_detached, + NULL, &libusb_rem_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult)); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); + } + + /* create notifications for attached devices */ + kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_attached, + NULL, &libusb_add_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult)); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); + } + + /* arm notifiers */ + darwin_clear_iterator (libusb_rem_device_iterator); + darwin_clear_iterator (libusb_add_device_iterator); + + usbi_dbg (NULL, "darwin event thread ready to receive events"); + + /* signal the main thread that the hotplug runloop has been created. */ + usbi_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfl = runloop; + libusb_darwin_acfls = libusb_shutdown_cfsource; + usbi_cond_signal (&libusb_darwin_at_cond); + usbi_mutex_unlock (&libusb_darwin_at_mutex); + + /* run the runloop */ + CFRunLoopRun(); + + usbi_dbg (NULL, "darwin event thread exiting"); + + /* signal the main thread that the hotplug runloop has finished. */ + usbi_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfls = NULL; + libusb_darwin_acfl = NULL; + usbi_cond_signal (&libusb_darwin_at_cond); + usbi_mutex_unlock (&libusb_darwin_at_mutex); + + /* remove the notification cfsource */ + CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* remove the shutdown cfsource */ + CFRunLoopRemoveSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); + + /* delete notification port */ + IONotificationPortDestroy (libusb_notification_port); + + /* delete iterators */ + IOObjectRelease (libusb_rem_device_iterator); + IOObjectRelease (libusb_add_device_iterator); + + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + + pthread_exit (NULL); +} + +/* cleanup function to destroy cached devices. must be called with a lock on darwin_cached_devices_mutex */ +static void darwin_cleanup_devices(void) { + struct darwin_cached_device *dev, *next; + + list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { + if (dev->refcount > 1) { + usbi_err(NULL, "device still referenced at libusb_exit"); + } + darwin_deref_cached_device(dev); + } +} + +/* must be called with a lock on darwin_cached_devices_mutex */ +static int darwin_first_time_init(void) { + if (NULL == darwin_cached_devices.next) { + list_init (&darwin_cached_devices); + } + + /* cache the interface versions that will be used. as a sanity check verify + * that the interface versions are non-zero. */ + const struct darwin_iokit_interface *interface_interface = get_interface_interface(); + const struct darwin_iokit_interface *device_interface = get_device_interface(); + if (0 == interface_interface->version || 0 == device_interface->version) { + usbi_err(NULL, "could not determine the device or interface interface to use with this version " + "of macOS (or MacOS X), current_running_version = %" PRIu32, get_running_version()); + return LIBUSB_ERROR_OTHER; + } + + if (!list_empty(&darwin_cached_devices)) { + usbi_err(NULL, "libusb_device reference not released on last exit. will not continue"); + return LIBUSB_ERROR_OTHER; + } + + int rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, NULL); + if (0 != rc) { + usbi_err (NULL, "could not create event thread, error %d", rc); + return LIBUSB_ERROR_OTHER; + } + + usbi_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_at_started = true; + while (NULL == libusb_darwin_acfl) { + usbi_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + } + + if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) { + libusb_darwin_acfl = NULL; + rc = LIBUSB_ERROR_OTHER; + } + usbi_mutex_unlock (&libusb_darwin_at_mutex); + + return rc; +} + +static int darwin_init_context(struct libusb_context *ctx) { + usbi_mutex_lock(&darwin_cached_devices_mutex); + + bool first_init = (1 == ++init_count); + + if (first_init) { + int rc = darwin_first_time_init(); + if (LIBUSB_SUCCESS != rc) { + usbi_mutex_unlock(&darwin_cached_devices_mutex); + return rc; + } + } + usbi_mutex_unlock(&darwin_cached_devices_mutex); + + return darwin_scan_devices (ctx); +} + +static int darwin_init(struct libusb_context *ctx) { + int rc = darwin_init_context(ctx); + if (LIBUSB_SUCCESS != rc) { + /* clean up any allocated resources */ + darwin_exit(ctx); + } + + return rc; +} + +static void darwin_exit (struct libusb_context *ctx) { + UNUSED(ctx); + + usbi_mutex_lock(&darwin_cached_devices_mutex); + if (0 == --init_count) { + /* stop the event runloop and wait for the thread to terminate. */ + usbi_mutex_lock (&libusb_darwin_at_mutex); + if (NULL != libusb_darwin_acfls) { + CFRunLoopSourceSignal (libusb_darwin_acfls); + CFRunLoopWakeUp (libusb_darwin_acfl); + while (libusb_darwin_acfl) + usbi_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + } + + if (libusb_darwin_at_started) { + pthread_join (libusb_darwin_at, NULL); + libusb_darwin_at_started = false; + } + usbi_mutex_unlock (&libusb_darwin_at_mutex); + + darwin_cleanup_devices (); + } + usbi_mutex_unlock(&darwin_cached_devices_mutex); +} + +static int darwin_get_device_string(struct libusb_device *dev, + enum libusb_device_string_type string_type, char *buffer, int length) { + + if (length <= 0) + return LIBUSB_ERROR_INVALID_PARAM; + + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + io_iterator_t deviceIterator; + io_service_t service; + kern_return_t kresult; + CFStringRef cf; + + kresult = usb_setup_device_iterator (&deviceIterator, priv->location); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + service = IOIteratorNext (deviceIterator); + IOObjectRelease(deviceIterator); + if (service == IO_OBJECT_NULL) + return LIBUSB_ERROR_NOT_FOUND; + + switch (string_type) { + case LIBUSB_DEVICE_STRING_MANUFACTURER: + cf = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBVendorString), kCFAllocatorDefault, 0); + break; + case LIBUSB_DEVICE_STRING_PRODUCT: + cf = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBProductString), kCFAllocatorDefault, 0); + break; + case LIBUSB_DEVICE_STRING_SERIAL_NUMBER: + cf = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); + break; + case LIBUSB_DEVICE_STRING_COUNT: /* intentional fall-through, avoid -Wswitch-enum */ + default: + IOObjectRelease(service); + return LIBUSB_ERROR_INVALID_PARAM; + } + + IOObjectRelease(service); + if (cf == NULL) + return LIBUSB_ERROR_NOT_FOUND; + + long cfUsedIndex = 0; + CFStringGetBytes(cf, CFRangeMake(0, CFStringGetLength(cf)), kCFStringEncodingUTF8, '?', false, + (uint8_t *) buffer, length - 1, &cfUsedIndex); + CFRelease(cf); + + if (cfUsedIndex <= 0) + return LIBUSB_ERROR_NOT_FOUND; + + buffer[cfUsedIndex] = '\0'; + return (int) cfUsedIndex + 1; +} + +static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + UInt8 i, numConfig; + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + + /* is there a simpler way to determine the index? */ + kresult = (*priv->device)->GetNumberOfConfigurations (priv->device, &numConfig); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + for (i = 0 ; i < numConfig ; i++) { + kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, i, &desc); + + if (kresult == kIOReturnSuccess && desc->bConfigurationValue == config_value) + return i; + } + + /* configuration not found */ + return LIBUSB_ERROR_NOT_FOUND; +} + +static int darwin_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + int config_index; + + if (0 == priv->active_config) + return LIBUSB_ERROR_NOT_FOUND; + + config_index = get_configuration_index (dev, priv->active_config); + if (config_index < 0) + return config_index; + + assert(config_index >= 0 && config_index <= UINT8_MAX); + return darwin_get_config_descriptor (dev, (UInt8)config_index, buffer, len); +} + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + int ret; + + if (!priv || !priv->device) + return LIBUSB_ERROR_OTHER; + + kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc); + if (kresult == kIOReturnSuccess) { + /* copy descriptor */ + if (libusb_le16_to_cpu(desc->wTotalLength) < len) + len = libusb_le16_to_cpu(desc->wTotalLength); + + memmove (buffer, desc, len); + } + + ret = darwin_to_libusb (kresult); + if (ret != LIBUSB_SUCCESS) + return ret; + + return (int) len; +} + +/* check whether the os has configured the device */ +static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { + usb_device_t darwin_device = dev->device; + + IOUSBConfigurationDescriptorPtr configDesc; + IOUSBFindInterfaceRequest request; + IOReturn kresult; + io_iterator_t interface_iterator; + io_service_t firstInterface; + + if (dev->dev_descriptor.bNumConfigurations < 1) { + usbi_err (ctx, "device has no configurations"); + return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */ + } + + /* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is + not usable anyway */ + if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) && + 0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) { + usbi_dbg (ctx, "ignoring configuration on root hub simulation"); + dev->active_config = 0; + return LIBUSB_SUCCESS; + } + + /* find the first configuration */ + kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc); + dev->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1; + + /* check if the device is already configured. there is probably a better way than iterating over the + to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices + might lock up on the device request) */ + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* iterate once */ + firstInterface = IOIteratorNext(interface_iterator); + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + if (firstInterface) { + IOObjectRelease (firstInterface); + + /* device is configured */ + if (dev->dev_descriptor.bNumConfigurations == 1) + /* to avoid problems with some devices get the configurations value from the configuration descriptor */ + dev->active_config = dev->first_config; + else + /* devices with more than one configuration should work with GetConfiguration */ + (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config); + } else + /* not configured */ + dev->active_config = 0; + + usbi_dbg (ctx, "active config: %u, first config: %u", dev->active_config, dev->first_config); + + return LIBUSB_SUCCESS; +} + +static IOReturn darwin_request_descriptor (usb_device_t device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { + IOUSBDevRequestTO req; + + assert(buffer_size <= UINT16_MAX); + + memset (buffer, 0, buffer_size); + + /* Set up request for descriptor/ */ + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = (UInt16)(desc << 8); + req.wIndex = desc_index; + req.wLength = (UInt16)buffer_size; + req.pData = buffer; + req.noDataTimeout = 20; + req.completionTimeout = 100; + + return (*device)->DeviceRequestTO (device, &req); +} + +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 */ + int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; + int is_open = 0; + IOReturn ret = 0, ret2; + UInt8 bDeviceClass; + UInt16 idProduct, idVendor; + + dev->can_enumerate = 0; + + (*device)->GetDeviceClass (device, &bDeviceClass); + (*device)->GetDeviceProduct (device, &idProduct); + (*device)->GetDeviceVendor (device, &idVendor); + + /* According to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some + * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still, + * to follow the spec as closely as possible, try opening the device */ + is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess); + + do { + /**** retrieve device descriptor ****/ + ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor)); + + if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType) + /* received an overrun error but we still received a device descriptor */ + ret = kIOReturnSuccess; + + if (kIOUSBVendorIDAppleComputer == idVendor) { + /* NTH: don't bother retrying or unsuspending Apple devices */ + break; + } + + if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations || + 0 == dev->dev_descriptor.bcdUSB)) { + /* work around for incorrectly configured devices */ + if (try_reconfigure && is_open) { + usbi_dbg(ctx, "descriptor appears to be invalid. resetting configuration before trying again..."); + + /* set the first configuration */ + (*device)->SetConfiguration(device, 1); + + /* don't try to reconfigure again */ + try_reconfigure = 0; + } + + ret = kIOUSBPipeStalled; + } + + if (kIOReturnSuccess != ret && is_open && try_unsuspend) { + /* device may be suspended. unsuspend it and try again */ +#if MAX_DEVICE_VERSION >= 320 + if (get_device_interface_version() >= 320) { + UInt32 info = 0; + + /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ + (void)(*IODEVICE_V(device, 320))->GetUSBDeviceInformation (device, &info); + + /* note that the device was suspended */ + if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info) + try_unsuspend = 1; + } +#endif + + if (try_unsuspend) { + /* try to unsuspend the device */ + ret2 = (*device)->USBDeviceSuspend (device, 0); + if (kIOReturnSuccess != ret2) { + /* prevent log spew from poorly behaving devices. this indicates the + os actually had trouble communicating with the device */ + usbi_dbg(ctx, "could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); + } else + unsuspended = 1; + + try_unsuspend = 0; + } + } + + if (kIOReturnSuccess != ret) { + 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 */ + struct timespec full_delay = {delay / 1000000, (delay * 1000) % 1000000000}; + nanosleep(&full_delay, NULL); + } + } while (kIOReturnSuccess != ret && retries--); + + if (unsuspended) + /* resuspend the device */ + (void)(*device)->USBDeviceSuspend (device, 1); + + if (is_open) + (void) (*device)->USBDeviceClose (device); + + if (ret != kIOReturnSuccess) { + /* a debug message was already printed out for this error */ + if (LIBUSB_CLASS_HUB == bDeviceClass) + usbi_dbg (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + idVendor, idProduct, darwin_error_str (ret), ret); + else + usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + idVendor, idProduct, darwin_error_str (ret), ret); + return darwin_to_libusb (ret); + } + + /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ + if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) { + /* not a valid device */ + usbi_warn (NULL, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device", + idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); + return LIBUSB_ERROR_NO_DEVICE; + } + + 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; + + return LIBUSB_SUCCESS; +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_device_port (io_service_t service, UInt8 *port) { + IOReturn kresult; + io_service_t parent; + bool ret = false; + + if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) { + return true; + } + + kresult = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent); + if (kIOReturnSuccess == kresult) { + /* + macOS 10 to 15 always had port number property as "port". + macOS 26 has changed it to "usb-port-number". + For the same binaries to run correctly on both macOS 15 and macOS 26+, + we have to detect os version at runtime and use correct port name property. + */ + uint32_t os_version = get_running_version(); + CFStringRef cfstrPort = os_version < 260000 ? CFSTR("port") : CFSTR("usb-port-number"); + ret = get_ioregistry_value_data (parent, cfstrPort, 1, port); + IOObjectRelease (parent); + } + + return ret; +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) { + /* Walk up the tree in the IOService plane until we find a parent that has a sessionID */ + io_service_t parent = service; + do { + IOReturn kresult = IORegistryEntryGetParentEntry (parent, kIOUSBPlane, &parent); + if (kresult != kIOReturnSuccess) { + break; + } + if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) { + /* Success */ + return true; + } + } while (true); + + /* We ran out of parents */ + return false; +} + +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; + UInt32 locationID = 0; + enum libusb_error ret = LIBUSB_SUCCESS; + usb_device_t device; + UInt8 port = 0; + + /* assuming sessionID != 0 normally (never seen it be 0) */ + *old_session_id = 0; + *cached_out = NULL; + + /* get some info from the io registry */ + (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID); + (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); + if (!get_device_port (service, &port)) { + usbi_dbg(ctx, "could not get connected port number"); + } + + usbi_dbg(ctx, "finding cached device for sessionID 0x%" PRIx64, sessionID); + + if (get_device_parent_sessionID(service, &parent_sessionID)) { + usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID); + } + + usbi_mutex_lock(&darwin_cached_devices_mutex); + do { + list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { + usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32 " against cached device with sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32, + sessionID, locationID, new_device->session, new_device->location); + if (new_device->location == locationID && atomic_load(&new_device->in_reenumerate)) { + 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(ctx, "using cached device for device"); + *cached_out = new_device; + break; + } + } + + if (*cached_out) + break; + + usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID); + + ret = darwin_device_from_service (ctx, service, &device); + if (LIBUSB_SUCCESS != ret) { + break; + } + + if (!(*old_session_id)) { + new_device = calloc (1, sizeof (*new_device)); + if (!new_device) { + ret = LIBUSB_ERROR_NO_MEM; + break; + } + + /* add this device to the cached device list */ + list_add(&new_device->list, &darwin_cached_devices); + + (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address); + + /* keep a reference to this device */ + darwin_ref_cached_device(new_device); + + (*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 + prevent them from being enumerated multiple times */ + *cached_out = new_device; + + 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(ctx, new_device); + if (ret) + break; + + if (new_device->can_enumerate) { + snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address, + libusb_le16_to_cpu (new_device->dev_descriptor.idVendor), + libusb_le16_to_cpu (new_device->dev_descriptor.idProduct), + new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass); + } + } while (0); + + usbi_mutex_unlock(&darwin_cached_devices_mutex); + + assert((ret == LIBUSB_SUCCESS) ? (*cached_out != NULL) : true); + + return ret; +} + +static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device, + UInt64 old_session_id) { + struct darwin_device_priv *priv; + struct libusb_device *dev = NULL; + UInt8 devSpeed; + enum libusb_error ret = LIBUSB_SUCCESS; + + do { + /* check current active configuration (and cache the first configuration value-- + which may be used by claim_interface) */ + ret = darwin_check_configuration (ctx, cached_device); + if (ret) + break; + + if (0 != old_session_id) { + 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 (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); + if (!dev) { + return LIBUSB_ERROR_NO_MEM; + } + + priv = usbi_get_device_priv(dev); + + 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; + } else { + priv = usbi_get_device_priv(dev); + } + + static_assert(sizeof(dev->device_descriptor) == sizeof(cached_device->dev_descriptor), + "mismatch between libusb and IOKit device descriptor sizes"); + memcpy(&dev->device_descriptor, &cached_device->dev_descriptor, LIBUSB_DT_DEVICE_SIZE); + 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); + } + + (*priv->dev->device)->GetDeviceSpeed (priv->dev->device, &devSpeed); + + switch (devSpeed) { + case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; + case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break; + case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 + case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break; +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + case kUSBDeviceSpeedSuperPlus: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break; +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 + case kUSBDeviceSpeedSuperPlusBy2: dev->speed = LIBUSB_SPEED_SUPER_PLUS_X2; break; +#endif + default: + usbi_warn (ctx, "Got unknown device speed %d", devSpeed); + } + + ret = usbi_sanitize_device (dev); + if (ret < 0) + break; + + 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); + + if (!atomic_load(&cached_device->in_reenumerate) && 0 == ret) { + usbi_connect_device (dev); + } else { + libusb_unref_device (dev); + } + + return ret; +} + +static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) { + struct darwin_cached_device *cached_device; + UInt64 old_session_id; + io_iterator_t deviceIterator; + io_service_t service; + IOReturn kresult; + int ret; + + kresult = usb_setup_device_iterator (&deviceIterator, 0); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + while ((service = IOIteratorNext (deviceIterator))) { + ret = darwin_get_cached_device (ctx, service, &cached_device, &old_session_id); + assert((ret >= 0) ? (cached_device != NULL) : true); + if (ret < 0 || !cached_device->can_enumerate) { + continue; + } + + (void) process_new_device (ctx, cached_device, old_session_id); + + IOObjectRelease(service); + } + + IOObjectRelease(deviceIterator); + + return LIBUSB_SUCCESS; +} + +static int darwin_open (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + + if (0 == dpriv->open_count) { + if (!dpriv->device) { + usbi_err(HANDLE_CTX(dev_handle), "device interface is NULL, cannot open"); + return LIBUSB_ERROR_NO_DEVICE; + } + /* try to open the device */ + kresult = (*dpriv->device)->USBDeviceOpenSeize (dpriv->device); + if (kresult != kIOReturnSuccess) { + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); + + if (kIOReturnExclusiveAccess != kresult) { + return darwin_to_libusb (kresult); + } + + /* it is possible to perform some actions on a device that is not open so do not return an error */ + priv->is_open = false; + } else { + priv->is_open = true; + } + + /* create async event source */ + kresult = (*dpriv->device)->CreateDeviceAsyncEventSource (dpriv->device, + &priv->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult)); + + if (priv->is_open) { + (*dpriv->device)->USBDeviceClose (dpriv->device); + } + + priv->is_open = false; + + return darwin_to_libusb (kresult); + } + + CFRetain (libusb_darwin_acfl); + + /* add the cfSource to the async run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); + } + + /* device opened successfully */ + dpriv->open_count++; + + usbi_dbg (HANDLE_CTX(dev_handle), "device open for access"); + + return 0; +} + +static void darwin_close (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + int i; + + if (dpriv->open_count == 0) { + /* something is probably very wrong if this is the case */ + usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!"); + return; + } + + 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++) + if (dev_handle->claimed_interfaces & (1U << i)) + libusb_release_interface (dev_handle, i); + + if (0 == dpriv->open_count) { + /* delete the device's async event source */ + if (priv->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode); + CFRelease (priv->cfSource); + priv->cfSource = NULL; + CFRelease (libusb_darwin_acfl); + } + + if (priv->is_open) { + /* close the device */ + kresult = (*dpriv->device)->USBDeviceClose(dpriv->device); + if (kresult != kIOReturnSuccess) { + /* Log the fact that we had a problem closing the file, however failing a + * close isn't really an error, so return success anyway */ + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); + } + } + } +} + +static int darwin_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + + *config = dpriv->active_config; + + return LIBUSB_SUCCESS; +} + +static enum libusb_error darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + uint8_t i; + + if (config == -1) + config = 0; + + /* Setting configuration will invalidate the interface, so we need + to reclaim it. First, dispose of existing interfaces, if any. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1U << i)) + darwin_release_interface (dev_handle, i); + + kresult = (*dpriv->device)->SetConfiguration (dpriv->device, (UInt8)config); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* Reclaim any interfaces. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1U << i)) + darwin_claim_interface (dev_handle, i); + + dpriv->active_config = (UInt8)config; + + return LIBUSB_SUCCESS; +} + +static IOReturn darwin_get_interface (usb_device_t darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { + IOUSBFindInterfaceRequest request; + IOReturn kresult; + io_iterator_t interface_iterator; + UInt8 bInterfaceNumber; + bool ret; + + *usbInterfacep = IO_OBJECT_NULL; + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult != kIOReturnSuccess) + return kresult; + + while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { + /* find the interface number */ + ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type, + &bInterfaceNumber); + + if (ret && bInterfaceNumber == ifc) { + break; + } + + (void) IOObjectRelease (*usbInterfacep); + } + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + return kIOReturnSuccess; +} + +static const struct libusb_interface_descriptor *get_interface_descriptor_by_number(struct libusb_device_handle *dev_handle, struct libusb_config_descriptor *conf_desc, int iface, uint8_t altsetting) +{ + int i; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + if (altsetting < conf_desc->interface[i].num_altsetting && conf_desc->interface[i].altsetting[altsetting].bInterfaceNumber == iface) { + return &conf_desc->interface[i].altsetting[altsetting]; + } + } + + usbi_err(HANDLE_CTX(dev_handle), "interface %d with altsetting %d not found for device", iface, (int)altsetting); + return NULL; +} + +static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + IOReturn kresult; + uint8_t numep; + int rc; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + + usbi_dbg (ctx, "building table of endpoints."); + + /* retrieve the total number of endpoints on this interface */ + kresult = (*IOINTERFACE(cInterface))->GetNumEndpoints(IOINTERFACE(cInterface), &numep); + if (kresult != kIOReturnSuccess) { + 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++) { + darwin_pipe_properties_t pipe_properties; + kresult = darwin_get_pipe_properties(cInterface, i, &pipe_properties); + if (kresult != kIOReturnSuccess) { + /* probably a buggy device. try to get the endpoint address from the descriptors */ + struct libusb_config_descriptor *config; + const struct libusb_interface_descriptor *if_desc; + const struct libusb_endpoint_descriptor *endpoint_desc; + UInt8 alt_setting; + + kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), &alt_setting); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface"); + return darwin_to_libusb (kresult); + } + + rc = libusb_get_active_config_descriptor (dev_handle->dev, &config); + if (LIBUSB_SUCCESS != rc) { + return rc; + } + + if_desc = get_interface_descriptor_by_number (dev_handle, config, iface, alt_setting); + if (if_desc == NULL) { + libusb_free_config_descriptor (config); + return LIBUSB_ERROR_NOT_FOUND; + } + + endpoint_desc = if_desc->endpoint + i - 1; + + cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress; + libusb_free_config_descriptor (config); + } else { + cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == pipe_properties.direction) << kUSBRqDirnShift) | + (pipe_properties.number & LIBUSB_ENDPOINT_ADDRESS_MASK)); + } + + 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); + } + + cInterface->num_endpoints = numep; + + return LIBUSB_SUCCESS; +} + +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + io_service_t usbInterface = IO_OBJECT_NULL; + IOReturn kresult; + enum libusb_error ret; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + + /* 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 (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 (ctx, "could not set configuration"); + return ret; + } + + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "darwin_get_interface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + } + + if (!usbInterface) { + usbi_info (ctx, "interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* get an interface to the device's interface */ + kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, &score); + + /* ignore release error */ + (void)IOObjectRelease (usbInterface); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + if (!plugInInterface) { + usbi_err (ctx, "plugin interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* Do the actual claim */ + kresult = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(get_interface_interface_id()), + (LPVOID *)&IOINTERFACE(cInterface)); + /* We no longer need the intermediate plug-in */ + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + if (!IOINTERFACE(cInterface)) { + usbi_err (ctx, "QueryInterface: returned null interface"); + return LIBUSB_ERROR_OTHER; + } + + /* claim the interface */ + kresult = (*IOINTERFACE(cInterface))->USBInterfaceOpen(IOINTERFACE(cInterface)); + if (kresult != kIOReturnSuccess) { + usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* update list of endpoints */ + ret = get_endpoints (dev_handle, iface); + if (ret) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (ctx, "could not build endpoint table"); + return ret; + } + + cInterface->cfSource = NULL; + + /* create async event source */ + kresult = (*IOINTERFACE(cInterface))->CreateInterfaceAsyncEventSource (IOINTERFACE(cInterface), &cInterface->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not create async event source"); + + /* can't continue without an async event source */ + (void)darwin_release_interface (dev_handle, iface); + + return darwin_to_libusb (kresult); + } + + /* add the cfSource to the async thread's run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + + usbi_dbg (ctx, "interface opened"); + + return LIBUSB_SUCCESS; +} + +static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + IOReturn kresult; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + /* Check to see if an interface is open */ + if (!IOINTERFACE(cInterface)) { + return LIBUSB_SUCCESS; + } + + /* clean up endpoint data */ + cInterface->num_endpoints = 0; + + /* delete the interface's async event source */ + if (cInterface->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + CFRelease (cInterface->cfSource); + cInterface->cfSource = NULL; + } + + kresult = (*IOINTERFACE(cInterface))->USBInterfaceClose(IOINTERFACE(cInterface)); + if (kresult != kIOReturnSuccess) + usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); + + ULONG refCount = (*IOINTERFACE(cInterface))->Release(IOINTERFACE(cInterface)); + if (refCount != 0) { + usbi_warn (HANDLE_CTX (dev_handle), "Release final refCount: %u", refCount); + } + + IOINTERFACE(cInterface) = NULL; + + 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 = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), ¤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; + enum libusb_error ret; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + if (!IOINTERFACE(cInterface)) { + return LIBUSB_ERROR_NO_DEVICE; + } + + kresult = (*IOINTERFACE(cInterface))->SetAlternateInterface (IOINTERFACE(cInterface), altsetting); + 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; + } + + usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult)); + + ret = darwin_to_libusb(kresult); + if (ret != LIBUSB_ERROR_PIPE) { + return ret; + } + + /* 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) { + /* current interface */ + struct darwin_interface *cInterface; + IOReturn kresult; + uint8_t pipeRef; + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + /* newer versions of darwin support clearing additional bits on the device's endpoint */ + kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef); + if (kresult != kIOReturnSuccess) + usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_restore_state (struct libusb_device_handle *dev_handle, uint8_t active_config, + unsigned long claimed_interfaces) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + 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; + + /* close and re-open the device */ + priv->is_open = false; + dpriv->open_count = 1; + + /* clean up open interfaces */ + darwin_close (dev_handle); + + /* re-open the device */ + ret = darwin_open (dev_handle); + dpriv->open_count = open_count; + if (LIBUSB_SUCCESS != ret) { + /* could not restore configuration */ + return LIBUSB_ERROR_NOT_FOUND; + } + + if (dpriv->active_config != 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 (ctx, "darwin/restore_state: could not restore configuration"); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg (ctx, "darwin/restore_state: reclaiming interfaces"); + + if (claimed_interfaces) { + for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) { + if (!(claimed_interfaces & (1U << iface))) { + continue; + } + + usbi_dbg (ctx, "darwin/restore_state: re-claiming interface %u", iface); + + ret = darwin_claim_interface (dev_handle, iface); + if (LIBUSB_SUCCESS != ret) { + usbi_dbg (ctx, "darwin/restore_state: could not claim interface %u", iface); + return LIBUSB_ERROR_NOT_FOUND; + } + + dev_handle->claimed_interfaces |= 1U << iface; + } + } + + usbi_dbg (ctx, "darwin/restore_state: device state restored"); + + return LIBUSB_SUCCESS; +} + +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; + uint8_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->device) { + usbi_warn(ctx, "darwin_reenumerate_device: device interface is NULL"); + return LIBUSB_ERROR_NO_DEVICE; + } + + if (atomic_exchange(&dpriv->in_reenumerate, true)) { + /* ack, two (or more) threads are trying to reset the device! abort! */ + return LIBUSB_ERROR_NOT_FOUND; + } + + /* store copies of descriptors so they can be compared after the reset */ + memcpy (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor)); + cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations); + + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + kresult = (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + if (kresult != kIOReturnSuccess) { + atomic_store(&dpriv->in_reenumerate, false); + return LIBUSB_ERROR_NOT_FOUND; + } + memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i])); + } + + /* if we need to release capture */ + if (get_running_version() >= 101000) { + 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, options); + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult)); + atomic_store(&dpriv->in_reenumerate, false); + return darwin_to_libusb (kresult); + } + + /* capture mode does not re-enumerate but it does require re-open */ + if (capture) { + usbi_dbg (ctx, "darwin/reenumerate_device: restoring state..."); + atomic_store(&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 (atomic_load(&dpriv->in_reenumerate)) { + const struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000}; + nanosleep (&delay, NULL); + + struct timespec now, delta; + usbi_get_monotonic_time(&now); + TIMESPEC_SUB(&now, &start, &delta); + unsigned long long elapsed_us = ((unsigned long long)delta.tv_sec * USEC_PER_SEC) + + ((unsigned long long)delta.tv_nsec / 1000ULL); + + if (elapsed_us >= DARWIN_REENUMERATE_TIMEOUT_US) { + usbi_err (ctx, "darwin/reenumerate_device: timeout waiting for reenumerate"); + atomic_store(&dpriv->in_reenumerate, false); + return LIBUSB_ERROR_TIMEOUT; + } + } + + /* compare descriptors */ + usbi_dbg (ctx, "darwin/reenumerate_device: checking whether descriptors changed"); + + if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor)) != 0) { + /* device descriptor changed. need to return not found. */ + usbi_dbg (ctx, "darwin/reenumerate_device: device descriptor changed"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + kresult = (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + if (kresult != kIOReturnSuccess || memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i])) != 0) { + usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg (ctx, "darwin/reenumerate_device: device reset complete. restoring state..."); + + return darwin_restore_state (dev_handle, active_config, claimed_interfaces); +} + +static int darwin_reset_device (struct libusb_device_handle *dev_handle) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + enum libusb_error ret; + +#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; + uint8_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); + + CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBHostMatchingPropertyInterfaceNumber), interfaceCF); + + 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; + } + + /* no driver */ + return 0; +} + +static void darwin_destroy_device(struct libusb_device *dev) { + struct darwin_device_priv *dpriv = usbi_get_device_priv(dev); + + if (dpriv->dev) { + /* need to hold the lock in case this is the last reference to the device */ + usbi_mutex_lock(&darwin_cached_devices_mutex); + darwin_deref_cached_device (dpriv->dev); + dpriv->dev = NULL; + usbi_mutex_unlock(&darwin_cached_devices_mutex); + } +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + IOReturn ret; + uint8_t pipeRef; + + struct darwin_interface *cInterface; + darwin_pipe_properties_t pipe_properties; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + ret = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties); + if (kIOReturnSuccess != ret) { + usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + return darwin_to_libusb (ret); + } + + if (0 != (transfer->length % pipe_properties.max_packet_size)) { + /* do not need a zero packet */ + transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET; + } + + /* submit the request */ + /* timeouts are unavailable on interrupt endpoints */ + if (pipe_properties.transfer_type == kUSBInterrupt) { + if (IS_XFERIN(transfer)) + ret = (*IOINTERFACE(cInterface))->ReadPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, darwin_async_io_callback, itransfer); + else + ret = (*IOINTERFACE(cInterface))->WritePipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, darwin_async_io_callback, itransfer); + } else { + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*IOINTERFACE(cInterface))->ReadPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); + else + ret = (*IOINTERFACE(cInterface))->WritePipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); + } + + if (ret) + usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + + return darwin_to_libusb (ret); +} + +#if MAX_INTERFACE_VERSION >= 550 +static int submit_stream_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_interface *cInterface; + uint8_t pipeRef; + IOReturn ret; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + if (get_interface_interface_version() < 550) { + usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version %d does not support bulk stream transfers", + get_interface_interface_version()); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*IOINTERFACE_V(cInterface, 550))->ReadStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id, + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); + else + ret = (*IOINTERFACE_V(cInterface, 550))->WriteStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id, + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); + + if (ret) + usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + + return darwin_to_libusb (ret); +} +#endif + +static int submit_iso_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + + IOReturn kresult; + uint8_t pipeRef; + UInt64 frame; + AbsoluteTime atTime; + int i; + darwin_pipe_properties_t pipe_properties; + struct darwin_interface *cInterface; + + /* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */ + if (tpriv->num_iso_packets != transfer->num_iso_packets) { + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } + + if (!tpriv->isoc_framelist) { + tpriv->num_iso_packets = transfer->num_iso_packets; + tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc ((size_t)transfer->num_iso_packets, sizeof(IOUSBIsocFrame)); + if (!tpriv->isoc_framelist) + return LIBUSB_ERROR_NO_MEM; + } + + /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ + for (i = 0 ; i < transfer->num_iso_packets ; i++) { + unsigned int length = transfer->iso_packet_desc[i].length; + assert(length <= UINT16_MAX); + tpriv->isoc_framelist[i].frReqCount = (UInt16)length; + } + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + /* determine the properties of this endpoint and the speed of the device */ + kresult = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties); + 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 = (*IOINTERFACE(cInterface))->GetBusFrameNumber(IOINTERFACE(cInterface), &frame, &atTime); + if (kresult != kIOReturnSuccess) { + usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult); + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + + return darwin_to_libusb (kresult); + } + + /* schedule for a frame a little in the future */ + frame += 4; + + if (cInterface->frames[transfer->endpoint] && frame < cInterface->frames[transfer->endpoint]) + frame = cInterface->frames[transfer->endpoint]; + + /* submit the request */ + if (IS_XFERIN(transfer)) + kresult = (*IOINTERFACE(cInterface))->ReadIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + else + kresult = (*IOINTERFACE(cInterface))->WriteIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + + if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed) + /* Full speed */ + cInterface->frames[transfer->endpoint] = frame + (UInt64)transfer->num_iso_packets * (1UL << (pipe_properties.interval - 1)); + else + /* High/super speed */ + cInterface->frames[transfer->endpoint] = frame + (UInt64)transfer->num_iso_packets * (1UL << (pipe_properties.interval - 1)) / 8; + + if (kresult != kIOReturnSuccess) { + usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(kresult)); + free (tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } + + return darwin_to_libusb (kresult); +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + + IOReturn kresult; + + memset(&tpriv->req, 0, sizeof(tpriv->req)); + + /* IOUSBDeviceInterface expects the request in cpu endianness */ + tpriv->req.bmRequestType = setup->bmRequestType; + tpriv->req.bRequest = setup->bRequest; + /* these values should be in bus order from libusb_fill_control_setup */ + tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue); + tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex); + tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength); + /* data is stored after the libusb control block */ + tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + tpriv->req.completionTimeout = transfer->timeout; + tpriv->req.noDataTimeout = transfer->timeout; + + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + /* all transfers in libusb-1.0 are async */ + + if (transfer->endpoint) { + struct darwin_interface *cInterface; + uint8_t pipeRef; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + kresult = (*IOINTERFACE(cInterface))->ControlRequestAsyncTO (IOINTERFACE(cInterface), pipeRef, + &(tpriv->req), darwin_async_io_callback, itransfer); + } else + /* control request on endpoint 0 */ + kresult = (*dpriv->device)->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); + + if (kresult != kIOReturnSuccess) + usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_submit_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: +#if MAX_INTERFACE_VERSION >= 550 + return submit_stream_transfer(itransfer); +#else + usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers"); + return LIBUSB_ERROR_NOT_SUPPORTED; +#endif + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int cancel_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + IOReturn kresult; + + usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe"); + + if (!dpriv->device) { + return LIBUSB_ERROR_NO_DEVICE; + } + + kresult = (*dpriv->device)->USBDeviceAbortPipeZero (dpriv->device); + + return darwin_to_libusb (kresult); +} + +static int darwin_abort_transfers (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + struct darwin_interface *cInterface; + 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 (ctx, "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!dpriv->device) { + return LIBUSB_ERROR_NO_DEVICE; + } + + usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef); + + /* abort transactions */ +#if MAX_INTERFACE_VERSION >= 550 + if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type && get_interface_interface_version() >= 550) { + kresult = (*IOINTERFACE_V(cInterface, 550))->AbortStreamsPipe (IOINTERFACE(cInterface), pipeRef, itransfer->stream_id); + } else +#endif + { + kresult = (*IOINTERFACE(cInterface))->AbortPipe (IOINTERFACE(cInterface), pipeRef); + } + + + if (get_interface_interface_version() <= 245) { + /* with older releases of IOUSBFamily the OS always clears the host side data toggle. for + consistency also clear the data toggle on the device. */ + usbi_dbg (ctx, "calling ClearPipeStallBothEnds to clear the data toggle bit"); + kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef); + } + + return darwin_to_libusb (kresult); +} + +static int darwin_cancel_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return cancel_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return darwin_abort_transfers (itransfer); + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) { + struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon; + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + + 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) { + struct darwin_interface *cInterface; + uint8_t pipeRef; + + (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface); + + (*IOINTERFACE(cInterface))->WritePipe (IOINTERFACE(cInterface), pipeRef, transfer->buffer, 0); + } + + tpriv->result = result; + tpriv->size = (UInt32) (uintptr_t) arg0; + + /* signal the core that this transfer is complete */ + usbi_signal_transfer_completion(itransfer); +} + +static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer *itransfer, IOReturn result) { + if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) + result = kIOUSBTransactionTimeout; + + struct libusb_context *ctx = ITRANSFER_CTX (itransfer); + + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_TRANSFER_COMPLETED; + case kIOReturnAborted: + return LIBUSB_TRANSFER_CANCELLED; + case kIOUSBPipeStalled: + usbi_dbg (ctx, "transfer error: pipe is stalled"); + return LIBUSB_TRANSFER_STALL; + case kIOReturnOverrun: + usbi_warn (ctx, "transfer error: data overrun"); + return LIBUSB_TRANSFER_OVERFLOW; + case kIOUSBTransactionTimeout: + usbi_warn (ctx, "transfer error: timed out"); + itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + return LIBUSB_TRANSFER_TIMED_OUT; + default: + usbi_warn (ctx, "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); + return LIBUSB_TRANSFER_ERROR; + } +} + +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; +#ifdef ENABLE_LOGGING + const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL}; +#endif + bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; + struct libusb_context *ctx = ITRANSFER_CTX (itransfer); + + if (transfer->type > max_transfer_type) { + usbi_err (ctx, "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (NULL == tpriv) { + usbi_err (ctx, "malformed request is missing transfer priv"); + return LIBUSB_ERROR_INVALID_PARAM; + } + + 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 || kIOUSBTransactionTimeout == tpriv->result) { + if (is_isoc && tpriv->isoc_framelist) { + /* copy isochronous results back */ + + for (int i = 0; i < transfer->num_iso_packets ; i++) { + struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; + lib_desc->status = darwin_transfer_status (itransfer, tpriv->isoc_framelist[i].frStatus); + lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; + } + } else if (!is_isoc) { + itransfer->transferred += tpriv->size; + } + } + + /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */ + return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result)); +} + +void usbi_get_monotonic_time(struct timespec *tp) { +/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */ +#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) + clock_gettime(CLOCK_MONOTONIC, tp); +#else + mach_timebase_info_data_t machTimeBaseInfo; + mach_timebase_info(&machTimeBaseInfo); + + uint64_t uptime = mach_absolute_time(); + uint64_t uptimeNano = uptime * machTimeBaseInfo.numer / machTimeBaseInfo.denom; + + uint64_t uptimeSeconds = uptimeNano / NSEC_PER_SEC; + uint64_t uptimeNanoRemainder = uptimeNano - (uptimeSeconds * NSEC_PER_SEC); + + tp->tv_sec = uptimeSeconds; + tp->tv_nsec = uptimeNanoRemainder; +#endif +} + +void usbi_get_real_time(struct timespec *tp) { +/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */ +#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) + clock_gettime(CLOCK_REALTIME, tp); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * NSEC_PER_USEC; +#endif +} + +#if MAX_INTERFACE_VERSION >= 550 +static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, + int num_endpoints) { + struct darwin_interface *cInterface; + UInt32 supportsStreams; + uint8_t pipeRef; + int rc, i; + + /* find the minimum number of supported streams on the endpoint list */ + for (i = 0 ; i < num_endpoints ; ++i) { + rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface); + if (0 != rc) { + return rc; + } + + (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams); + if (num_streams > supportsStreams) + num_streams = supportsStreams; + } + + /* it is an error if any endpoint in endpoints does not support streams */ + if (0 == num_streams) + return LIBUSB_ERROR_INVALID_PARAM; + + /* create the streams */ + for (i = 0 ; i < num_endpoints ; ++i) { + (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface); + + rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, num_streams); + if (kIOReturnSuccess != rc) + return darwin_to_libusb(rc); + } + + assert(num_streams <= INT_MAX); + return (int)num_streams; +} + +static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) { + struct darwin_interface *cInterface; + UInt32 supportsStreams; + uint8_t pipeRef; + int rc; + + for (int i = 0 ; i < num_endpoints ; ++i) { + rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface); + if (0 != rc) + return rc; + + (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams); + if (0 == supportsStreams) + return LIBUSB_ERROR_INVALID_PARAM; + + rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, 0); + if (kIOReturnSuccess != rc) + return darwin_to_libusb(rc); + } + + return LIBUSB_SUCCESS; +} +#endif + +#if MAX_INTERFACE_VERSION >= 700 + +/* macOS APIs for getting entitlement values */ + +#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 +#include +#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_mutex); + (*dpriv->device)->Release(dpriv->device); + err = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service, &dpriv->device); + usbi_mutex_unlock(&darwin_cached_devices_mutex); + + 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 (get_interface_interface_version() < 700) { + 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 (get_interface_interface_version() < 700) { + 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 = USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, + .init = darwin_init, + .exit = darwin_exit, + .set_option = NULL, + .get_device_list = NULL, + .get_device_string = darwin_get_device_string, + .hotplug_poll = darwin_hotplug_poll, + .wrap_sys_device = NULL, + .open = darwin_open, + .close = darwin_close, + .get_active_config_descriptor = darwin_get_active_config_descriptor, + .get_config_descriptor = darwin_get_config_descriptor, + .get_config_descriptor_by_value = NULL, + .get_configuration = darwin_get_configuration, + .set_configuration = darwin_set_configuration, + +#if MAX_INTERFACE_VERSION >= 700 + .claim_interface = darwin_capture_claim_interface, + .release_interface = darwin_capture_release_interface, +#else + .claim_interface = darwin_claim_interface, + .release_interface = darwin_release_interface, +#endif + + .set_interface_altsetting = darwin_set_interface_altsetting, + .clear_halt = darwin_clear_halt, + .reset_device = darwin_reset_device, + +#if MAX_INTERFACE_VERSION >= 550 + .alloc_streams = darwin_alloc_streams, + .free_streams = darwin_free_streams, +#endif + + .dev_mem_alloc = NULL, + .dev_mem_free = NULL, + .kernel_driver_active = darwin_kernel_driver_active, + +#if MAX_INTERFACE_VERSION >= 700 + .detach_kernel_driver = darwin_detach_kernel_driver, + .attach_kernel_driver = darwin_attach_kernel_driver, +#endif + + .destroy_device = darwin_destroy_device, + + .submit_transfer = darwin_submit_transfer, + .cancel_transfer = darwin_cancel_transfer, + .clear_transfer_priv = NULL, + .handle_events = NULL, + .handle_transfer_completion = darwin_handle_transfer_completion, + + .context_priv_size = 0, + .device_priv_size = sizeof(struct darwin_device_priv), + .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), + .transfer_priv_size = sizeof(struct darwin_transfer_priv), +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.h new file mode 100644 index 00000000..a7a9fbea --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/darwin_usb.h @@ -0,0 +1,158 @@ +/* + * darwin backend for libusb 1.0 + * Copyright © 2008-2023 Nathan Hjelm + * Copyright © 2019-2023 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined(LIBUSB_DARWIN_H) +#define LIBUSB_DARWIN_H + +#include + +#include "libusbi.h" + +#include +#include +#include +#include + +#if defined(HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H) +#include +#endif + +/* IOUSBInterfaceInferface */ + +#if defined(kIOUSBInterfaceInterfaceID800) +#define MAX_INTERFACE_VERSION 800 +#elif defined(kIOUSBInterfaceInterfaceID700) +#define MAX_INTERFACE_VERSION 700 +#elif defined(kIOUSBInterfaceInterfaceID650) +#define MAX_INTERFACE_VERSION 650 +#elif defined(kIOUSBInterfaceInterfaceID550) +#define MAX_INTERFACE_VERSION 550 +#elif defined(kIOUSBInterfaceInterfaceID245) +#define MAX_INTERFACE_VERSION 245 +#else +#define MAX_INTERFACE_VERSION 220 +#endif + +/* set to the minimum version and casted up as needed. */ +typedef IOUSBInterfaceInterface220 **usb_interface_t; + +#define IOINTERFACE0(darwin_interface, version) ((IOUSBInterfaceInterface ## version **) (darwin_interface)->interface) +#define IOINTERFACE_V(darwin_interface, version) IOINTERFACE0(darwin_interface, version) +#define IOINTERFACE(darwin_interface) ((darwin_interface)->interface) + +/* IOUSBDeviceInterface */ + +#if defined(kIOUSBDeviceInterfaceID650) +#define MAX_DEVICE_VERSION 650 +#elif defined(kIOUSBDeviceInterfaceID500) +#define MAX_DEVICE_VERSION 500 +#elif defined(kIOUSBDeviceInterfaceID320) +#define MAX_DEVICE_VERSION 320 +#elif defined(kIOUSBDeviceInterfaceID300) +#define MAX_DEVICE_VERSION 300 +#elif defined(kIOUSBDeviceInterfaceID245) +#define MAX_DEVICE_VERSION 245 +#else +#define MAX_DEVICE_VERSION 197 +#endif + +/* set to the minimum version and casted up as needed */ +typedef IOUSBDeviceInterface197 **usb_device_t; + +#define IODEVICE0(darwin_device, version) ((IOUSBDeviceInterface ## version **)(darwin_device)) +#define IODEVICE_V(darwin_device, version) IODEVICE0(darwin_device, version) + +#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 + +/* returns the current macOS version in a format similar to the + * MAC_OS_X_VERSION_MIN_REQUIRED macro. + * Examples: + * 10.1.5 -> 100105 + * 13.3.0 -> 130300 + */ +uint32_t get_running_version(void); + +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; +typedef IONotificationPortRef io_notification_port_t; + +/* private structures */ +struct darwin_cached_device { + struct list_head list; + IOUSBDeviceDescriptor dev_descriptor; + UInt32 location; + UInt64 parent_session; + UInt64 session; + 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; + atomic_bool in_reenumerate; + int capture_count; +}; + +struct darwin_device_priv { + struct darwin_cached_device *dev; +}; + +struct darwin_interface { + usb_interface_t interface; + uint8_t num_endpoints; + CFRunLoopSourceRef cfSource; + uint64_t frames[256]; + uint8_t endpoint_addrs[USB_MAXENDPOINTS]; +}; + +struct darwin_device_handle_priv { + bool is_open; + CFRunLoopSourceRef cfSource; + + struct darwin_interface interfaces[USB_MAXINTERFACES]; +}; + +struct darwin_transfer_priv { + /* Isoc */ + IOUSBIsocFrame *isoc_framelist; + int num_iso_packets; + + /* Control */ + IOUSBDevRequestTO req; + + /* Bulk */ + + /* Completion status */ + IOReturn result; + UInt32 size; +}; + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/emscripten_webusb.cpp b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/emscripten_webusb.cpp new file mode 100644 index 00000000..63d0b10a --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/emscripten_webusb.cpp @@ -0,0 +1,896 @@ +/* + * Copyright © 2021 Google LLC + * Copyright © 2023 Ingvar Stepanyan + * + * 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 + * + * Authors: + * Ingvar Stepanyan + */ + +#include + +static_assert((__EMSCRIPTEN_major__ * 100 * 100 + __EMSCRIPTEN_minor__ * 100 + + __EMSCRIPTEN_tiny__) >= 30148, + "Emscripten 3.1.48 or newer is required."); + +#include +#include +#include + +#include +#include + +#include "libusbi.h" + +using namespace emscripten; + +#ifdef _REENTRANT +#include +#include +#include + +static ProxyingQueue queue; +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wshadow" + +namespace { + +// clang-format off +EM_JS(EM_VAL, usbi_em_promise_catch, (EM_VAL handle), { + let promise = Emval.toValue(handle); + promise = promise.then( + value => ({error : 0, value}), + error => { + console.error(error); + let errorCode = -99; // LIBUSB_ERROR_OTHER + if (error instanceof DOMException) { + const ERROR_CODES = { + // LIBUSB_ERROR_IO + NetworkError : -1, + // LIBUSB_ERROR_INVALID_PARAM + DataError : -2, + TypeMismatchError : -2, + IndexSizeError : -2, + // LIBUSB_ERROR_ACCESS + SecurityError : -3, + // LIBUSB_ERROR_NOT_FOUND + NotFoundError : -5, + // LIBUSB_ERROR_BUSY + InvalidStateError : -6, + // LIBUSB_ERROR_TIMEOUT + TimeoutError : -7, + // LIBUSB_ERROR_INTERRUPTED + AbortError : -10, + // LIBUSB_ERROR_NOT_SUPPORTED + NotSupportedError : -12, + }; + errorCode = ERROR_CODES[error.name] ?? errorCode; + } else if (error instanceof RangeError || error instanceof TypeError) { + errorCode = -2; // LIBUSB_ERROR_INVALID_PARAM + } + return {error: errorCode, value: undefined}; + } + ); + return Emval.toHandle(promise); +}); + +EM_JS(void, usbi_em_copy_from_dataview, (void* dst, EM_VAL src), { + src = Emval.toValue(src); + src = new Uint8Array(src.buffer, src.byteOffset, src.byteLength); + HEAPU8.set(src, dst); +}); + +// Our implementation proxies operations from multiple threads to the same +// underlying USBDevice on the main thread. This can lead to issues when +// multiple threads try to open/close the same device at the same time. +// +// First, since open/close operations are asynchronous in WebUSB, we can end up +// with multiple open/close operations in flight at the same time, which can +// lead to unpredictable outcome (e.g. device got closed but opening succeeded +// right before that). +// +// Second, since multiple threads are allowed to have their own handles to the +// same device, we need to keep track of number of open handles and close the +// device only when the last handle is closed. +// +// We fix both of these issues by using a shared promise chain that executes +// open and close operations sequentially and keeps track of the reference count +// in each promise's result. This way, we can ensure that only one open/close +// operation is in flight at any given time. Note that we don't need to worry +// about all other operations because they're preconditioned on the device being +// open and having at least 1 reference anyway. +EM_JS(EM_VAL, usbi_em_device_safe_open_close, (EM_VAL device, bool open), { + device = Emval.toValue(device); + const symbol = Symbol.for('libusb.open_close_chain'); + let promiseChain = device[symbol] ?? Promise.resolve(0); + device[symbol] = promiseChain = promiseChain.then(async refCount => { + if (open) { + if (!refCount++) { + await device.open(); + } + } else { + if (!--refCount) { + await device.close(); + } + } + return refCount; + }); + return Emval.toHandle(promiseChain); +}); +// clang-format on + +libusb_transfer_status getTransferStatus(const val& transfer_result) { + auto status = transfer_result["status"].as(); + if (status == "ok") { + return LIBUSB_TRANSFER_COMPLETED; + } else if (status == "stall") { + return LIBUSB_TRANSFER_STALL; + } else if (status == "babble") { + return LIBUSB_TRANSFER_OVERFLOW; + } else { + return LIBUSB_TRANSFER_ERROR; + } +} + +// Note: this assumes that `dst` is valid for at least `src.byteLength` bytes. +// This is true for all results returned from WebUSB as we pass max length to +// the transfer APIs. +void copyFromDataView(void* dst, const val& src) { + usbi_em_copy_from_dataview(dst, src.as_handle()); +} + +auto getUnsharedMemoryView(void* src, size_t len) { + auto view = typed_memory_view(len, (uint8_t*)src); +#ifdef _REENTRANT + // Unfortunately, TypedArrays backed by SharedArrayBuffers are not accepted + // by most Web APIs, trading off guaranteed thread-safety for performance + // loss. The usual workaround is to copy them into a new TypedArray, which + // is what we do here via the `.slice()` method. + return val(view).call("slice"); +#else + // Non-threaded builds can avoid the copy penalty. + return view; +#endif +} + +// A helper that proxies a function call to the main thread if not already +// there. This is a wrapper around Emscripten's raw proxying API with couple of +// high-level improvements, namely support for destroying lambda on the target +// thread as well as custom return types. +template +auto runOnMain(Func&& func) { +#ifdef _REENTRANT + if (!emscripten_is_main_runtime_thread()) { + if constexpr (std::is_same_v, void>) { + bool proxied = + queue.proxySync(emscripten_main_runtime_thread_id(), [&func] { + // Capture func by reference and move into a local variable + // to render the captured func inert on the first (and only) + // call. This way it can be safely destructed on the main + // thread instead of the current one when this call + // finishes. TODO: remove this when + // https://github.com/emscripten-core/emscripten/issues/20610 + // is fixed. + auto func_ = std::move(func); + func_(); + }); + assert(proxied); + return; + } else { + // A storage for the result of the function call. + // TODO: remove when + // https://github.com/emscripten-core/emscripten/issues/20611 is + // implemented. + std::optional> result; + runOnMain( + [&result, func = std::move(func)] { result.emplace(func()); }); + return std::move(result.value()); + } + } +#endif + return func(); +} + +// C++ struct representation for `{value, error}` object used by `CaughtPromise` +// below. +struct PromiseResult { + int error; + val value; + + PromiseResult() = delete; + PromiseResult(PromiseResult&&) = default; + + PromiseResult(val&& result) + : error(result["error"].as()), value(result["value"]) {} + + ~PromiseResult() { + // make sure value is freed on the thread it exists on + runOnMain([value = std::move(value)] {}); + } +}; + +struct CaughtPromise : val { + CaughtPromise(val&& promise) + : val(wrapPromiseWithCatch(std::move(promise))) {} + + using AwaitResult = PromiseResult; + +private: + + // Wrap promise with conversion from some value T to `{value: T, error: + // number}`. + static val wrapPromiseWithCatch(val&& promise) { + auto handle = promise.as_handle(); + handle = usbi_em_promise_catch(handle); + return val::take_ownership(handle); + } +}; + +#define co_await_try(promise) \ + ({ \ + PromiseResult result = co_await CaughtPromise(promise); \ + if (result.error) { \ + co_return result.error; \ + } \ + std::move(result.value); \ + }) + +// A helper that runs an asynchronous callback when the promise is resolved. +template +val promiseThen(Promise&& promise, OnResult&& onResult) { + // Save captures from the callback while we can, or they'll be destructed. + // https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 + auto onResult_ = std::move(onResult); + onResult_(co_await promise); + co_return val::undefined(); +} + +// A helper that runs an asynchronous function on the main thread and blocks the +// current thread until the promise is resolved (via Asyncify "blocking" if +// already on the main thread or regular blocking otherwise). +template +static std::invoke_result_t::AwaitResult awaitOnMain(Func&& func) { +#ifdef _REENTRANT + if (!emscripten_is_main_runtime_thread()) { + // If we're on a different thread, we can't use main thread's Asyncify + // as multiple threads might be fighting for its state; instead, use + // proxying to synchronously block the current thread until the promise + // is complete. + std::optional::AwaitResult> result; + queue.proxySyncWithCtx( + emscripten_main_runtime_thread_id(), + [&result, &func](ProxyingQueue::ProxyingCtx ctx) { + // Same as `func` in `runOnMain`, move to destruct on the first + // call. + auto func_ = std::move(func); + promiseThen( + func_(), + [&result, ctx = std::move(ctx)](auto&& result_) mutable { + result.emplace(std::move(result_)); + ctx.finish(); + }); + }); + return std::move(result.value()); + } +#endif + // If we're already on the main thread, use Asyncify to block until the + // promise is resolved. + return func().await(); +} + +// A helper that makes a control transfer given a setup pointer (assumed to be +// followed by data payload for out-transfers). +val makeControlTransferPromise(const val& dev, libusb_control_setup* setup) { + auto params = val::object(); + + const char* request_type = "unknown"; + // See LIBUSB_REQ_TYPE in windows_winusb.h (or docs for `bmRequestType`). + switch (setup->bmRequestType & (0x03 << 5)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + request_type = "standard"; + break; + case LIBUSB_REQUEST_TYPE_CLASS: + request_type = "class"; + break; + case LIBUSB_REQUEST_TYPE_VENDOR: + request_type = "vendor"; + break; + } + params.set("requestType", request_type); + + const char* recipient = "other"; + switch (setup->bmRequestType & 0x0f) { + case LIBUSB_RECIPIENT_DEVICE: + recipient = "device"; + break; + case LIBUSB_RECIPIENT_INTERFACE: + recipient = "interface"; + break; + case LIBUSB_RECIPIENT_ENDPOINT: + recipient = "endpoint"; + break; + } + params.set("recipient", recipient); + + params.set("request", setup->bRequest); + params.set("value", setup->wValue); + params.set("index", setup->wIndex); + + if (setup->bmRequestType & LIBUSB_ENDPOINT_IN) { + return dev.call("controlTransferIn", params, setup->wLength); + } else { + return dev.call("controlTransferOut", params, + getUnsharedMemoryView(setup + 1, setup->wLength)); + } +} + +// Smart pointer for managing pointers to places allocated by libusb inside its +// backend structures. +template +struct ValPtr { + template + void emplace(Args&&... args) { + new (ptr) T(std::forward(args)...); + } + + const T& operator*() const { return *ptr; } + T& operator*() { return *ptr; } + + const T* operator->() const { return ptr; } + T* operator->() { return ptr; } + + void free() { ptr->~T(); } + + T take() { + auto value = std::move(*ptr); + free(); + return value; + } + +protected: + + ValPtr(void* ptr) : ptr(static_cast(ptr)) {} + +private: + + // Note: this is not a heap-allocated pointer, but a pointer to a part + // of the backend structure allocated by libusb itself. + T* ptr; +}; + +struct CachedDevice; + +struct WebUsbDevicePtr : ValPtr { +public: + + WebUsbDevicePtr(libusb_device* dev) : ValPtr(usbi_get_device_priv(dev)) {} + WebUsbDevicePtr(libusb_device_handle* handle) + : WebUsbDevicePtr(handle->dev) {} +}; + +struct WebUsbTransferPtr : ValPtr { +public: + + WebUsbTransferPtr(usbi_transfer* itransfer) + : ValPtr(usbi_get_transfer_priv(itransfer)) {} +}; + +enum class OpenClose : bool { + Open = true, + Close = false, +}; + +struct CachedDevice { + CachedDevice() = delete; + CachedDevice(CachedDevice&&) = delete; + + // Fill in the device descriptor and configurations by reading them from the + // WebUSB device. + static val initFromDevice(val&& web_usb_dev, libusb_device* libusb_dev) { + auto cachedDevicePtr = WebUsbDevicePtr(libusb_dev); + cachedDevicePtr.emplace(std::move(web_usb_dev)); + bool must_close = false; + val result = co_await cachedDevicePtr->initFromDeviceWithoutClosing( + libusb_dev, must_close); + if (must_close) { + co_await_try(cachedDevicePtr->safeOpenCloseAssumingMainThread( + OpenClose::Close)); + } + co_return std::move(result); + } + + const val& getDeviceAssumingMainThread() const { return device; } + + uint8_t getActiveConfigValue() const { + return runOnMain([&] { + auto web_usb_config = device["configuration"]; + return web_usb_config.isNull() + ? 0 + : web_usb_config["configurationValue"].as(); + }); + } + + usbi_configuration_descriptor* getConfigDescriptor(uint8_t config_id) { + return config_id < configurations.size() + ? configurations[config_id].get() + : nullptr; + } + + usbi_configuration_descriptor* findConfigDescriptorByValue( + uint8_t config_id) const { + for (auto& config : configurations) { + if (config->bConfigurationValue == config_id) { + return config.get(); + } + } + return nullptr; + } + + int copyConfigDescriptor(const usbi_configuration_descriptor* config, + void* buf, + size_t buf_len) { + auto len = std::min(buf_len, (size_t)config->wTotalLength); + memcpy(buf, config, len); + return len; + } + + template + int awaitOnMain(const char* methodName, Args&&... args) const { + return ::awaitOnMain([&] { + return CaughtPromise(device.call( + methodName, std::forward(args)...)); + }) + .error; + } + + ~CachedDevice() { + runOnMain([device = std::move(device)] {}); + } + + CaughtPromise safeOpenCloseAssumingMainThread(OpenClose open) { + return val::take_ownership(usbi_em_device_safe_open_close( + device.as_handle(), static_cast(open))); + } + + int safeOpenCloseOnMain(OpenClose open) { + return ::awaitOnMain([this, open] { + return safeOpenCloseAssumingMainThread(open); + }) + .error; + } + +private: + + val device; + std::vector> configurations; + + CaughtPromise requestDescriptor(libusb_descriptor_type desc_type, + uint8_t desc_index, + uint16_t max_length) const { + libusb_control_setup setup = { + .bmRequestType = LIBUSB_ENDPOINT_IN, + .bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR, + .wValue = (uint16_t)((desc_type << 8) | desc_index), + .wIndex = 0, + .wLength = max_length, + }; + return makeControlTransferPromise(device, &setup); + } + + // Implementation of the `CachedDevice::initFromDevice` above. This is a + // separate function just because we need to close the device on exit if + // we opened it successfully, and we can't use an async operation (`close`) + // in RAII destructor. + val initFromDeviceWithoutClosing(libusb_device* dev, bool& must_close) { + co_await_try(safeOpenCloseAssumingMainThread(OpenClose::Open)); + + // Can't use RAII to close on exit as co_await is not permitted in + // destructors (yet: + // https://github.com/cplusplus/papers/issues/445), so use a good + // old boolean + a wrapper instead. + must_close = true; + + { + auto result = co_await_try( + requestDescriptor(LIBUSB_DT_DEVICE, 0, LIBUSB_DT_DEVICE_SIZE)); + if (auto error = getTransferStatus(result)) { + co_return error; + } + copyFromDataView(&dev->device_descriptor, result["data"]); + } + + // Infer the device speed (which is not yet provided by WebUSB) from + // the descriptor. + if (dev->device_descriptor.bMaxPacketSize0 == + /* actually means 2^9, only valid for superspeeds */ 9) { + dev->speed = dev->device_descriptor.bcdUSB >= 0x0310 + ? LIBUSB_SPEED_SUPER_PLUS + : LIBUSB_SPEED_SUPER; + } else if (dev->device_descriptor.bcdUSB >= 0x0200) { + dev->speed = LIBUSB_SPEED_HIGH; + } else if (dev->device_descriptor.bMaxPacketSize0 > 8) { + dev->speed = LIBUSB_SPEED_FULL; + } else { + dev->speed = LIBUSB_SPEED_LOW; + } + + if (auto error = usbi_sanitize_device(dev)) { + co_return error; + } + + auto configurations_len = dev->device_descriptor.bNumConfigurations; + configurations.reserve(configurations_len); + for (uint8_t j = 0; j < configurations_len; j++) { + // Read descriptor header first to discover target length. + auto config_header_result = co_await_try( + requestDescriptor(LIBUSB_DT_CONFIG, j, LIBUSB_DT_CONFIG_SIZE)); + if (auto error = getTransferStatus(config_header_result)) { + co_return error; + } + union usbi_config_desc_buf config_header = {}; + copyFromDataView(config_header.buf, config_header_result["data"]); + if (config_header.desc.bDescriptorType != LIBUSB_DT_CONFIG || + config_header.desc.bLength < LIBUSB_DT_CONFIG_SIZE) { + co_return LIBUSB_ERROR_IO; + } + + auto config_total_length = + libusb_le16_to_cpu(config_header.desc.wTotalLength); + if (config_total_length < LIBUSB_DT_CONFIG_SIZE) { + co_return LIBUSB_ERROR_IO; + } + + auto config_result = co_await_try( + requestDescriptor(LIBUSB_DT_CONFIG, j, config_total_length)); + if (auto error = getTransferStatus(config_result)) { + co_return error; + } + + auto configVal = config_result["data"]; + if (configVal["byteLength"].as() < config_total_length) { + co_return LIBUSB_ERROR_IO; + } + + auto configLen = configVal["byteLength"].as(); + auto& config = configurations.emplace_back( + (usbi_configuration_descriptor*)::operator new(configLen)); + copyFromDataView(config.get(), configVal); + } + + co_return (int) LIBUSB_SUCCESS; + } + + CachedDevice(val device) : device(std::move(device)) {} + + friend struct ValPtr; +}; + +unsigned long getDeviceSessionId(val& web_usb_device) { + thread_local const val SessionIdSymbol = + val::global("Symbol")(val("libusb.session_id")); + + val session_id_val = web_usb_device[SessionIdSymbol]; + if (!session_id_val.isUndefined()) { + return session_id_val.as(); + } + + // If the device doesn't have a session ID, it means we haven't seen + // it before. Generate a new session ID for it. We can associate an + // incrementing ID with the `USBDevice` object itself. It's + // guaranteed to be alive and, thus, stable as long as the device is + // connected, even between different libusb invocations. See + // https://github.com/WICG/webusb/issues/241. + + static unsigned long next_session_id = 0; + + web_usb_device.set(SessionIdSymbol, next_session_id); + return next_session_id++; +} + +val getDeviceList(libusb_context* ctx, discovered_devs** devs) { + // Check if browser supports USB + val navigator_usb = val::global("navigator")["usb"]; + if (navigator_usb == val::undefined()) { + co_return (int) LIBUSB_ERROR_NOT_SUPPORTED; + } + // C++ equivalent of `await navigator.usb.getDevices()`. Note: at this point + // we must already have some devices exposed - caller must have called + // `await navigator.usb.requestDevice(...)` in response to user interaction + // before going to LibUSB. Otherwise this list will be empty. + auto web_usb_devices = + co_await_try(navigator_usb.call("getDevices")); + for (auto&& web_usb_device : web_usb_devices) { + auto session_id = getDeviceSessionId(web_usb_device); + + auto dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + usbi_err(ctx, "failed to allocate a new device structure"); + continue; + } + + auto statusVal = co_await CachedDevice::initFromDevice( + std::move(web_usb_device), dev); + if (auto error = statusVal.as()) { + usbi_err(ctx, "failed to read device information: %s", + libusb_error_name(error)); + libusb_unref_device(dev); + continue; + } + + // We don't have real buses in WebUSB, just pretend everything + // is on bus 1. + dev->bus_number = 1; + // This can wrap around but it's the best approximation of a stable + // device address and port number we can provide. + dev->device_address = dev->port_number = (uint8_t)session_id; + + usbi_connect_device(dev); + } + *devs = discovered_devs_append(*devs, dev); + libusb_unref_device(dev); + } + co_return (int) LIBUSB_SUCCESS; +} + +int em_get_device_list(libusb_context* ctx, discovered_devs** devs) { + // No need to wrap into CaughtPromise as we catch all individual ops in the + // inner implementation and return just the error code. We do need a custom + // promise type to ensure conversion to int happens on the main thread + // though. + struct IntPromise : val { + IntPromise(val&& promise) : val(std::move(promise)) {} + + struct AwaitResult { + int error; + + AwaitResult(val&& result) : error(result.as()) {} + }; + }; + + return awaitOnMain( + [ctx, devs] { return IntPromise(getDeviceList(ctx, devs)); }) + .error; +} + +int em_open(libusb_device_handle* handle) { + return WebUsbDevicePtr(handle)->safeOpenCloseOnMain(OpenClose::Open); +} + +void em_close(libusb_device_handle* handle) { + // LibUSB API doesn't allow us to handle an error here, but we still need to + // wait for the promise to make sure that subsequent attempt to reopen the + // same device doesn't fail with a "device busy" error. + if (auto error = + WebUsbDevicePtr(handle)->safeOpenCloseOnMain(OpenClose::Close)) { + usbi_err(handle->dev->ctx, "failed to close device: %s", + libusb_error_name(error)); + } +} + +int em_get_active_config_descriptor(libusb_device* dev, void* buf, size_t len) { + auto& cached_device = *WebUsbDevicePtr(dev); + auto config_value = cached_device.getActiveConfigValue(); + if (auto config = cached_device.findConfigDescriptorByValue(config_value)) { + return cached_device.copyConfigDescriptor(config, buf, len); + } else { + return LIBUSB_ERROR_NOT_FOUND; + } +} + +int em_get_config_descriptor(libusb_device* dev, + uint8_t config_id, + void* buf, + size_t len) { + auto& cached_device = *WebUsbDevicePtr(dev); + if (auto config = cached_device.getConfigDescriptor(config_id)) { + return cached_device.copyConfigDescriptor(config, buf, len); + } else { + return LIBUSB_ERROR_NOT_FOUND; + } +} + +int em_get_configuration(libusb_device_handle* dev_handle, + uint8_t* config_value) { + *config_value = WebUsbDevicePtr(dev_handle)->getActiveConfigValue(); + return LIBUSB_SUCCESS; +} + +int em_get_config_descriptor_by_value(libusb_device* dev, + uint8_t config_value, + void** buf) { + auto& cached_device = *WebUsbDevicePtr(dev); + if (auto config = cached_device.findConfigDescriptorByValue(config_value)) { + *buf = config; + return config->wTotalLength; + } else { + return LIBUSB_ERROR_NOT_FOUND; + } +} + +int em_set_configuration(libusb_device_handle* dev_handle, int config) { + return WebUsbDevicePtr(dev_handle)->awaitOnMain("selectConfiguration", config); +} + +int em_claim_interface(libusb_device_handle* handle, uint8_t iface) { + return WebUsbDevicePtr(handle)->awaitOnMain("claimInterface", iface); +} + +int em_release_interface(libusb_device_handle* handle, uint8_t iface) { + return WebUsbDevicePtr(handle)->awaitOnMain("releaseInterface", iface); +} + +int em_set_interface_altsetting(libusb_device_handle* handle, + uint8_t iface, + uint8_t altsetting) { + return WebUsbDevicePtr(handle)->awaitOnMain("selectAlternateInterface", + iface, altsetting); +} + +int em_clear_halt(libusb_device_handle* handle, unsigned char endpoint) { + std::string direction = endpoint & LIBUSB_ENDPOINT_IN ? "in" : "out"; + endpoint &= LIBUSB_ENDPOINT_ADDRESS_MASK; + + return WebUsbDevicePtr(handle)->awaitOnMain("clearHalt", direction, + endpoint); +} + +int em_reset_device(libusb_device_handle* handle) { + return WebUsbDevicePtr(handle)->awaitOnMain("reset"); +} + +void em_destroy_device(libusb_device* dev) { + WebUsbDevicePtr(dev).free(); +} + +int em_submit_transfer(usbi_transfer* itransfer) { + return runOnMain([itransfer] { + auto transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + auto& web_usb_device = WebUsbDevicePtr(transfer->dev_handle) + ->getDeviceAssumingMainThread(); + val transfer_promise; + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: { + transfer_promise = makeControlTransferPromise( + web_usb_device, + libusb_control_transfer_get_setup(transfer)); + break; + } + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: { + auto endpoint = + transfer->endpoint & LIBUSB_ENDPOINT_ADDRESS_MASK; + + if (IS_XFERIN(transfer)) { + transfer_promise = web_usb_device.call( + "transferIn", endpoint, transfer->length); + } else { + auto data = getUnsharedMemoryView(transfer->buffer, + transfer->length); + transfer_promise = + web_usb_device.call("transferOut", endpoint, data); + } + + break; + } + // TODO: add implementation for isochronous transfers too. + default: + return LIBUSB_ERROR_NOT_SUPPORTED; + } + // Not a coroutine because we don't want to block on this promise, just + // schedule an asynchronous callback. + promiseThen(CaughtPromise(std::move(transfer_promise)), + [itransfer](auto&& result) { + WebUsbTransferPtr(itransfer).emplace(std::move(result)); + usbi_signal_transfer_completion(itransfer); + }); + return LIBUSB_SUCCESS; + }); +} + +void em_clear_transfer_priv(usbi_transfer* itransfer) { + WebUsbTransferPtr(itransfer).free(); +} + +int em_cancel_transfer(usbi_transfer* itransfer) { + return LIBUSB_SUCCESS; +} + +int em_handle_transfer_completion(usbi_transfer* itransfer) { + libusb_transfer_status status = runOnMain([itransfer] { + auto transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + // Take ownership of the transfer result, as `em_clear_transfer_priv` is + // not called automatically for completed transfers and we must free it + // to avoid leaks. + + auto result = WebUsbTransferPtr(itransfer).take(); + + if (itransfer->state_flags & USBI_TRANSFER_CANCELLING) { + return LIBUSB_TRANSFER_CANCELLED; + } + + if (result.error) { + return LIBUSB_TRANSFER_ERROR; + } + + auto& value = result.value; + + void* dataDest; + unsigned char endpointDir; + + if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) { + dataDest = libusb_control_transfer_get_data(transfer); + endpointDir = + libusb_control_transfer_get_setup(transfer)->bmRequestType; + } else { + dataDest = transfer->buffer; + endpointDir = transfer->endpoint; + } + + if (endpointDir & LIBUSB_ENDPOINT_IN) { + auto data = value["data"]; + if (!data.isNull()) { + itransfer->transferred = data["byteLength"].as(); + copyFromDataView(dataDest, data); + } + } else { + itransfer->transferred = value["bytesWritten"].as(); + } + + return getTransferStatus(value); + }); + + // Invoke user's handlers outside of the main thread to reduce pressure. + return status == LIBUSB_TRANSFER_CANCELLED + ? usbi_handle_transfer_cancellation(itransfer) + : usbi_handle_transfer_completion(itransfer, status); +} + +} // namespace + +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +extern "C" const usbi_os_backend usbi_backend = { + .name = "Emscripten + WebUSB backend", + .caps = 0, + .get_device_list = em_get_device_list, + .open = em_open, + .close = em_close, + .get_active_config_descriptor = em_get_active_config_descriptor, + .get_config_descriptor = em_get_config_descriptor, + .get_config_descriptor_by_value = em_get_config_descriptor_by_value, + .get_configuration = em_get_configuration, + .set_configuration = em_set_configuration, + .claim_interface = em_claim_interface, + .release_interface = em_release_interface, + .set_interface_altsetting = em_set_interface_altsetting, + .clear_halt = em_clear_halt, + .reset_device = em_reset_device, + .destroy_device = em_destroy_device, + .submit_transfer = em_submit_transfer, + .cancel_transfer = em_cancel_transfer, + .clear_transfer_priv = em_clear_transfer_priv, + .handle_transfer_completion = em_handle_transfer_completion, + .device_priv_size = sizeof(CachedDevice), + .transfer_priv_size = sizeof(PromiseResult), +}; + +#pragma clang diagnostic pop diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.c new file mode 100644 index 00000000..bf984b91 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.c @@ -0,0 +1,340 @@ +/* + * libusb event abstraction on POSIX platforms + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include +#include +#ifdef HAVE_EVENTFD +#include +#endif +#ifdef HAVE_TIMERFD +#include +#endif + +#ifdef __EMSCRIPTEN__ +/* On Emscripten `pipe` does not conform to the spec and does not block + * until events are available, which makes it unusable for event system + * and often results in deadlocks when `pipe` is in a loop like it is + * in libusb. + * + * Therefore use a custom event system based on browser event emitters. */ +#include +#include +#include + +EM_ASYNC_JS(void, em_libusb_wait_async, (const _Atomic int* ptr, int expected_value, int timeout), { + await Atomics.waitAsync(HEAP32, ptr >> 2, expected_value, timeout).value; +}); + +static void em_libusb_wait(const _Atomic int *ptr, int expected_value, int timeout) +{ + if (emscripten_is_main_runtime_thread()) { + em_libusb_wait_async(ptr, expected_value, timeout); + } else { + emscripten_atomic_wait_u32((int*)ptr, expected_value, 1000000LL * timeout); + } +} +#endif +#include + +#ifdef HAVE_EVENTFD +#define EVENT_READ_FD(e) ((e)->eventfd) +#define EVENT_WRITE_FD(e) ((e)->eventfd) +#else +#define EVENT_READ_FD(e) ((e)->pipefd[0]) +#define EVENT_WRITE_FD(e) ((e)->pipefd[1]) +#endif + +#ifdef HAVE_NFDS_T +typedef nfds_t usbi_nfds_t; +#else +typedef unsigned int usbi_nfds_t; +#endif + +int usbi_create_event(usbi_event_t *event) +{ +#ifdef HAVE_EVENTFD + event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (event->eventfd == -1) { + usbi_err(NULL, "failed to create eventfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +#else +#if defined(HAVE_PIPE2) + int ret = pipe2(event->pipefd, O_CLOEXEC); +#else + int ret = pipe(event->pipefd); +#endif + + if (ret != 0) { + usbi_err(NULL, "failed to create pipe, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + +#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC) + ret = fcntl(event->pipefd[0], F_GETFD); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + + ret = fcntl(event->pipefd[1], F_GETFD); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } +#endif + + ret = fcntl(event->pipefd[1], F_GETFL); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno); + goto err_close_pipe; + } + + return 0; + +err_close_pipe: + close(event->pipefd[1]); + close(event->pipefd[0]); + return LIBUSB_ERROR_OTHER; +#endif +} + +void usbi_destroy_event(usbi_event_t *event) +{ +#ifdef HAVE_EVENTFD + if (close(event->eventfd) == -1) + usbi_warn(NULL, "failed to close eventfd, errno=%d", errno); +#else + if (close(event->pipefd[1]) == -1) + usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno); + if (close(event->pipefd[0]) == -1) + usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno); +#endif +} + +void usbi_signal_event(usbi_event_t *event) +{ + uint64_t dummy = 1; + ssize_t r; + + r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) + usbi_warn(NULL, "event write failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 1; + emscripten_atomic_notify(&event->has_event, EMSCRIPTEN_NOTIFY_ALL_WAITERS); +#endif +} + +void usbi_clear_event(usbi_event_t *event) +{ + uint64_t dummy; + ssize_t r; + + r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) + usbi_warn(NULL, "event read failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 0; +#endif +} + +#ifdef HAVE_TIMERFD +int usbi_create_timer(usbi_timer_t *timer) +{ + timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + if (timer->timerfd == -1) { + usbi_warn(NULL, "failed to create timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +void usbi_destroy_timer(usbi_timer_t *timer) +{ + if (close(timer->timerfd) == -1) + usbi_warn(NULL, "failed to close timerfd, errno=%d", errno); +} + +int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout) +{ + const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } }; + + if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) { + usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +int usbi_disarm_timer(usbi_timer_t *timer) +{ + const struct itimerspec it = { { 0, 0 }, { 0, 0 } }; + + if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) { + usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} +#endif + +int usbi_alloc_event_data(struct libusb_context *ctx) +{ + struct usbi_event_source *ievent_source; + struct pollfd *fds; + size_t i = 0; + + if (ctx->event_data) { + free(ctx->event_data); + ctx->event_data = NULL; + } + + ctx->event_data_cnt = 0; + for_each_event_source(ctx, ievent_source) + ctx->event_data_cnt++; + + fds = calloc(ctx->event_data_cnt, sizeof(*fds)); + if (!fds) + return LIBUSB_ERROR_NO_MEM; + + for_each_event_source(ctx, ievent_source) { + fds[i].fd = ievent_source->data.os_handle; + fds[i].events = ievent_source->data.poll_events; + i++; + } + + ctx->event_data = fds; + return 0; +} + +int usbi_wait_for_events(struct libusb_context *ctx, + struct usbi_reported_events *reported_events, int timeout_ms) +{ + struct pollfd *fds = ctx->event_data; + usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt; + int internal_fds, num_ready; + + usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); +#ifdef __EMSCRIPTEN__ + /* Emscripten's poll doesn't actually block, so we need to use an + * out-of-band waiting signal. */ + em_libusb_wait(&ctx->event.has_event, 0, timeout_ms); + /* Emscripten ignores timeout_ms, but set it to 0 for future-proofing + * in case they ever implement real poll. */ + timeout_ms = 0; +#endif + num_ready = poll(fds, nfds, timeout_ms); + usbi_dbg(ctx, "poll() returned %d", num_ready); + if (num_ready == 0) { + if (usbi_using_timer(ctx)) + goto done; + return LIBUSB_ERROR_TIMEOUT; + } else if (num_ready == -1) { + if (errno == EINTR) + return LIBUSB_ERROR_INTERRUPTED; + usbi_err(ctx, "poll() failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + /* fds[0] is always the internal signalling event */ + if (fds[0].revents) { + reported_events->event_triggered = 1; + num_ready--; + } else { + reported_events->event_triggered = 0; + } + +#ifdef HAVE_OS_TIMER + /* on timer configurations, fds[1] is the timer */ + if (usbi_using_timer(ctx) && fds[1].revents) { + reported_events->timer_triggered = 1; + num_ready--; + } else { + reported_events->timer_triggered = 0; + } +#endif + + if (!num_ready) + goto done; + + /* the backend will never need to attempt to handle events on the + * library's internal file descriptors, so we determine how many are + * in use internally for this context and skip these when passing any + * remaining pollfds to the backend. */ + internal_fds = usbi_using_timer(ctx) ? 2 : 1; + fds += internal_fds; + nfds -= internal_fds; + + usbi_mutex_lock(&ctx->event_data_lock); + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) { + struct usbi_event_source *ievent_source; + + for_each_removed_event_source(ctx, ievent_source) { + usbi_nfds_t n; + + for (n = 0; n < nfds; n++) { + if (ievent_source->data.os_handle != fds[n].fd) + continue; + if (!fds[n].revents) + continue; + /* pollfd was removed between the creation of the fds array and + * here. remove triggered revent as it is no longer relevant. */ + usbi_dbg(ctx, "fd %d was removed, ignoring raised events", fds[n].fd); + fds[n].revents = 0; + num_ready--; + break; + } + } + } + usbi_mutex_unlock(&ctx->event_data_lock); + + if (num_ready) { + assert(num_ready > 0); + reported_events->event_data = fds; + reported_events->event_data_count = (unsigned int)nfds; + } + +done: + reported_events->num_ready = num_ready; + return LIBUSB_SUCCESS; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.h new file mode 100644 index 00000000..4bd7f0fa --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_posix.h @@ -0,0 +1,62 @@ +/* + * libusb event abstraction on POSIX platforms + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_EVENTS_POSIX_H +#define LIBUSB_EVENTS_POSIX_H + +#include + +typedef int usbi_os_handle_t; +#define USBI_OS_HANDLE_FORMAT_STRING "fd %d" + +#ifdef HAVE_EVENTFD +typedef struct usbi_event { + int eventfd; +} usbi_event_t; +#define USBI_EVENT_OS_HANDLE(e) ((e)->eventfd) +#define USBI_EVENT_POLL_EVENTS POLLIN +#define USBI_INVALID_EVENT { -1 } +#else +typedef struct usbi_event { + int pipefd[2]; +#ifdef __EMSCRIPTEN__ + _Atomic int has_event; +#endif +} usbi_event_t; +#define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0]) +#define USBI_EVENT_POLL_EVENTS POLLIN +#define USBI_INVALID_EVENT { { -1, -1 } } +#endif + +#ifdef HAVE_TIMERFD +#define HAVE_OS_TIMER 1 +typedef struct usbi_timer { + int timerfd; +} usbi_timer_t; +#define USBI_TIMER_OS_HANDLE(t) ((t)->timerfd) +#define USBI_TIMER_POLL_EVENTS POLLIN + +static inline int usbi_timer_valid(usbi_timer_t *timer) +{ + return timer->timerfd >= 0; +} +#endif + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.c new file mode 100644 index 00000000..f22bebc0 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.c @@ -0,0 +1,214 @@ +/* + * libusb event abstraction on Microsoft Windows + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libusbi.h" +#include "windows_common.h" + +int usbi_create_event(usbi_event_t *event) +{ + event->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (event->hEvent == NULL) { + usbi_err(NULL, "CreateEvent failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +void usbi_destroy_event(usbi_event_t *event) +{ + if (!CloseHandle(event->hEvent)) + usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0)); +} + +void usbi_signal_event(usbi_event_t *event) +{ + if (!SetEvent(event->hEvent)) + usbi_warn(NULL, "SetEvent failed: %s", windows_error_str(0)); +} + +void usbi_clear_event(usbi_event_t *event) +{ + if (!ResetEvent(event->hEvent)) + usbi_warn(NULL, "ResetEvent failed: %s", windows_error_str(0)); +} + +#ifdef HAVE_OS_TIMER +int usbi_create_timer(usbi_timer_t *timer) +{ + timer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + if (timer->hTimer == NULL) { + usbi_warn(NULL, "CreateWaitableTimer failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +void usbi_destroy_timer(usbi_timer_t *timer) +{ + if (!CloseHandle(timer->hTimer)) + usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0)); +} + +int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout) +{ + struct timespec systime, remaining; + FILETIME filetime; + LARGE_INTEGER dueTime; + + /* Transfer timeouts are based on the monotonic clock and the waitable + * timers on the system clock. This requires a conversion between the + * two, so we calculate the remaining time relative to the monotonic + * clock and calculate an absolute system time for the timer expiration. + * Note that if the timeout has already passed, the remaining time will + * be negative and thus an absolute system time in the past will be set. + * This works just as intended because the timer becomes signalled + * immediately. */ + usbi_get_monotonic_time(&systime); + + TIMESPEC_SUB(timeout, &systime, &remaining); + + GetSystemTimeAsFileTime(&filetime); + dueTime.LowPart = filetime.dwLowDateTime; + dueTime.HighPart = filetime.dwHighDateTime; + dueTime.QuadPart += (remaining.tv_sec * 10000000LL) + (remaining.tv_nsec / 100LL); + + if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) { + usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +int usbi_disarm_timer(usbi_timer_t *timer) +{ + LARGE_INTEGER dueTime; + + /* A manual-reset waitable timer will stay in the signalled state until + * another call to SetWaitableTimer() is made. It is possible that the + * timer has already expired by the time we come in to disarm it, so to + * be entirely sure the timer is disarmed and not in the signalled state, + * we will set it with an impossibly large expiration and immediately + * cancel. */ + dueTime.QuadPart = LLONG_MAX; + if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) { + usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + if (!CancelWaitableTimer(timer->hTimer)) { + usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} +#endif + +int usbi_alloc_event_data(struct libusb_context *ctx) +{ + struct usbi_event_source *ievent_source; + HANDLE *handles; + size_t i = 0; + + /* Event sources are only added during usbi_io_init(). We should not + * be running this function again if the event data has already been + * allocated. */ + if (ctx->event_data) { + usbi_warn(ctx, "program assertion failed - event data already allocated"); + return LIBUSB_ERROR_OTHER; + } + + ctx->event_data_cnt = 0; + for_each_event_source(ctx, ievent_source) + ctx->event_data_cnt++; + + /* We only expect up to two HANDLEs to wait on, one for the internal + * signalling event and the other for the timer. */ + if (ctx->event_data_cnt != 1 && ctx->event_data_cnt != 2) { + usbi_err(ctx, "program assertion failed - expected exactly 1 or 2 HANDLEs"); + return LIBUSB_ERROR_OTHER; + } + + handles = calloc(ctx->event_data_cnt, sizeof(HANDLE)); + if (!handles) + return LIBUSB_ERROR_NO_MEM; + + for_each_event_source(ctx, ievent_source) { + handles[i] = ievent_source->data.os_handle; + i++; + } + + ctx->event_data = handles; + return 0; +} + +int usbi_wait_for_events(struct libusb_context *ctx, + struct usbi_reported_events *reported_events, int timeout_ms) +{ + HANDLE *handles = ctx->event_data; + DWORD num_handles = (DWORD)ctx->event_data_cnt; + DWORD result; + + 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(ctx, "WaitForMultipleObjects() returned %lu", ULONG_CAST(result)); + if (result == WAIT_TIMEOUT) { + if (usbi_using_timer(ctx)) + goto done; + return LIBUSB_ERROR_TIMEOUT; + } else if (result == WAIT_FAILED) { + usbi_err(ctx, "WaitForMultipleObjects() failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + result -= WAIT_OBJECT_0; + + /* handles[0] is always the internal signalling event */ + if (result == 0) + reported_events->event_triggered = 1; + else + reported_events->event_triggered = 0; + +#ifdef HAVE_OS_TIMER + /* on timer configurations, handles[1] is the timer */ + if (usbi_using_timer(ctx)) { + /* The WaitForMultipleObjects() function reports the index of + * the first object that became signalled. If the internal + * signalling event was reported, we need to also check and + * report whether the timer is in the signalled state. */ + if (result == 1 || WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) + reported_events->timer_triggered = 1; + else + reported_events->timer_triggered = 0; + } else { + reported_events->timer_triggered = 0; + } +#endif + +done: + /* no events are ever reported to the backend */ + reported_events->num_ready = 0; + return LIBUSB_SUCCESS; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.h new file mode 100644 index 00000000..0c5e0b03 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/events_windows.h @@ -0,0 +1,46 @@ +/* + * libusb event abstraction on Microsoft Windows + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_EVENTS_WINDOWS_H +#define LIBUSB_EVENTS_WINDOWS_H + +typedef HANDLE usbi_os_handle_t; +#define USBI_OS_HANDLE_FORMAT_STRING "HANDLE %p" + +typedef struct usbi_event { + HANDLE hEvent; +} usbi_event_t; +#define USBI_EVENT_OS_HANDLE(e) ((e)->hEvent) +#define USBI_EVENT_POLL_EVENTS 0 +#define USBI_INVALID_EVENT { INVALID_HANDLE_VALUE } + +#define HAVE_OS_TIMER 1 +typedef struct usbi_timer { + HANDLE hTimer; +} usbi_timer_t; +#define USBI_TIMER_OS_HANDLE(t) ((t)->hTimer) +#define USBI_TIMER_POLL_EVENTS 0 + +static inline int usbi_timer_valid(usbi_timer_t *timer) +{ + return timer->hTimer != NULL; +} + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_pollfs.cpp b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_pollfs.cpp new file mode 100644 index 00000000..b85edf7c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_pollfs.cpp @@ -0,0 +1,372 @@ +/* + * Copyright 2007-2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "haiku_usb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class WatchedEntry { +public: + WatchedEntry(BMessenger *, entry_ref *); + ~WatchedEntry(); + bool EntryCreated(entry_ref *ref); + bool EntryRemoved(ino_t node); + bool InitCheck(); + +private: + BMessenger* fMessenger; + node_ref fNode; + bool fIsDirectory; + USBDevice* fDevice; + WatchedEntry* fEntries; + WatchedEntry* fLink; + bool fInitCheck; +}; + + +class RosterLooper : public BLooper { +public: + RosterLooper(USBRoster *); + void Stop(); + virtual void MessageReceived(BMessage *); + bool InitCheck(); + +private: + USBRoster* fRoster; + WatchedEntry* fRoot; + BMessenger* fMessenger; + bool fInitCheck; +}; + + +WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref) + : fMessenger(messenger), + fIsDirectory(false), + fDevice(NULL), + fEntries(NULL), + fLink(NULL), + fInitCheck(false) +{ + BEntry entry(ref); + entry.GetNodeRef(&fNode); + + BDirectory directory; + if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) { + fIsDirectory = true; + + while (directory.GetNextEntry(&entry) >= B_OK) { + if (entry.GetRef(ref) < B_OK) + continue; + + WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); + if (child == NULL) + continue; + if (child->InitCheck() == false) { + delete child; + continue; + } + + child->fLink = fEntries; + fEntries = child; + } + + watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); + } + else { + if (strncmp(ref->name, "raw", 3) == 0) + return; + + BPath path, parent_path; + entry.GetPath(&path); + fDevice = new(std::nothrow) USBDevice(path.Path()); + if (fDevice != NULL && fDevice->InitCheck() == true) { + // Add this new device to each active context's device list + struct libusb_context *ctx; + unsigned long session_id = (unsigned long)&fDevice; + + usbi_mutex_lock(&active_contexts_lock); + for_each_context(ctx) { + struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_dbg(NULL, "using previously allocated device with location %lu", session_id); + libusb_unref_device(dev); + continue; + } + usbi_dbg(NULL, "allocating new device with location %lu", session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) { + usbi_dbg(NULL, "device allocation failed"); + continue; + } + *((USBDevice **)usbi_get_device_priv(dev)) = fDevice; + + // Calculate pseudo-device-address + int addr, tmp; + if (strcmp(path.Leaf(), "hub") == 0) + tmp = 100; //Random Number + else + sscanf(path.Leaf(), "%d", &tmp); + addr = tmp + 1; + path.GetParent(&parent_path); + while (strcmp(parent_path.Leaf(), "usb") != 0) { + sscanf(parent_path.Leaf(), "%d", &tmp); + addr += tmp + 1; + parent_path.GetParent(&parent_path); + } + sscanf(path.Path(), "/dev/bus/usb/%hhu", &dev->bus_number); + dev->device_address = addr - (dev->bus_number + 1); + + static_assert(sizeof(dev->device_descriptor) == sizeof(usb_device_descriptor), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, fDevice->Descriptor(), LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + if (usbi_sanitize_device(dev) < 0) { + usbi_dbg(NULL, "device sanitization failed"); + libusb_unref_device(dev); + continue; + } + usbi_connect_device(dev); + } + usbi_mutex_unlock(&active_contexts_lock); + } + else if (fDevice) { + delete fDevice; + fDevice = NULL; + return; + } + } + fInitCheck = true; +} + + +WatchedEntry::~WatchedEntry() +{ + if (fIsDirectory) { + watch_node(&fNode, B_STOP_WATCHING, *fMessenger); + + WatchedEntry *child = fEntries; + while (child) { + WatchedEntry *next = child->fLink; + delete child; + child = next; + } + } + + if (fDevice) { + // Remove this device from each active context's device list + struct libusb_context *ctx; + struct libusb_device *dev; + unsigned long session_id = (unsigned long)&fDevice; + + usbi_mutex_lock(&active_contexts_lock); + for_each_context(ctx) { + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev != NULL) { + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } else { + usbi_dbg(ctx, "device with location %lu not found", session_id); + } + } + usbi_mutex_static_unlock(&active_contexts_lock); + delete fDevice; + } +} + + +bool +WatchedEntry::EntryCreated(entry_ref *ref) +{ + if (!fIsDirectory) + return false; + + if (ref->directory != fNode.node) { + WatchedEntry *child = fEntries; + while (child) { + if (child->EntryCreated(ref)) + return true; + child = child->fLink; + } + return false; + } + + WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); + if (child == NULL) + return false; + child->fLink = fEntries; + fEntries = child; + return true; +} + + +bool +WatchedEntry::EntryRemoved(ino_t node) +{ + if (!fIsDirectory) + return false; + + WatchedEntry *child = fEntries; + WatchedEntry *lastChild = NULL; + while (child) { + if (child->fNode.node == node) { + if (lastChild) + lastChild->fLink = child->fLink; + else + fEntries = child->fLink; + delete child; + return true; + } + + if (child->EntryRemoved(node)) + return true; + + lastChild = child; + child = child->fLink; + } + return false; +} + + +bool +WatchedEntry::InitCheck() +{ + return fInitCheck; +} + + +RosterLooper::RosterLooper(USBRoster *roster) + : BLooper("LibusbRoster Looper"), + fRoster(roster), + fRoot(NULL), + fMessenger(NULL), + fInitCheck(false) +{ + BEntry entry("/dev/bus/usb"); + if (!entry.Exists()) { + usbi_err(NULL, "usb_raw not published"); + return; + } + + Run(); + fMessenger = new(std::nothrow) BMessenger(this); + if (fMessenger == NULL) { + usbi_err(NULL, "error creating BMessenger object"); + return; + } + + if (Lock()) { + entry_ref ref; + entry.GetRef(&ref); + fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref); + Unlock(); + if (fRoot == NULL) + return; + if (fRoot->InitCheck() == false) { + delete fRoot; + fRoot = NULL; + return; + } + } + fInitCheck = true; +} + + +void +RosterLooper::Stop() +{ + Lock(); + delete fRoot; + delete fMessenger; + Quit(); +} + + +void +RosterLooper::MessageReceived(BMessage *message) +{ + int32 opcode; + if (message->FindInt32("opcode", &opcode) < B_OK) + return; + + switch (opcode) { + case B_ENTRY_CREATED: + { + dev_t device; + ino_t directory; + const char *name; + if (message->FindInt32("device", &device) < B_OK || + message->FindInt64("directory", &directory) < B_OK || + message->FindString("name", &name) < B_OK) + break; + + entry_ref ref(device, directory, name); + fRoot->EntryCreated(&ref); + break; + } + case B_ENTRY_REMOVED: + { + ino_t node; + if (message->FindInt64("node", &node) < B_OK) + break; + fRoot->EntryRemoved(node); + break; + } + } +} + + +bool +RosterLooper::InitCheck() +{ + return fInitCheck; +} + + +USBRoster::USBRoster() + : fLooper(NULL) +{ +} + + +USBRoster::~USBRoster() +{ + Stop(); +} + + +int +USBRoster::Start() +{ + if (fLooper == NULL) { + fLooper = new(std::nothrow) RosterLooper(this); + if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) { + if (fLooper) + fLooper = NULL; + return LIBUSB_ERROR_OTHER; + } + } + return LIBUSB_SUCCESS; +} + + +void +USBRoster::Stop() +{ + if (fLooper) { + ((RosterLooper *)fLooper)->Stop(); + fLooper = NULL; + } +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb.h new file mode 100644 index 00000000..2dd5177c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb.h @@ -0,0 +1,113 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "libusbi.h" +#include "haiku_usb_raw.h" + +using namespace std; + +class USBDevice; +class USBDeviceHandle; +class USBTransfer; + +class USBDevice { +public: + USBDevice(const char *); + virtual ~USBDevice(); + const char* Location() const; + uint8 CountConfigurations() const; + const usb_device_descriptor* Descriptor() const; + const usb_configuration_descriptor* ConfigurationDescriptor(uint8) const; + const usb_configuration_descriptor* ActiveConfiguration() const; + uint8 EndpointToIndex(uint8) const; + uint8 EndpointToInterface(uint8) const; + int ClaimInterface(uint8); + int ReleaseInterface(uint8); + int CheckInterfacesFree(uint8); + void SetActiveConfiguration(uint8); + uint8 ActiveConfigurationIndex() const; + bool InitCheck(); +private: + int Initialise(); + unsigned int fClaimedInterfaces; // Max Interfaces can be 32. Using a bitmask + usb_device_descriptor fDeviceDescriptor; + unsigned char** fConfigurationDescriptors; + uint8 fActiveConfiguration; + char* fPath; + map fConfigToIndex; + map* fEndpointToIndex; + map* fEndpointToInterface; + bool fInitCheck; +}; + +class USBDeviceHandle { +public: + USBDeviceHandle(USBDevice *dev); + virtual ~USBDeviceHandle(); + int ClaimInterface(uint8); + int ReleaseInterface(uint8); + int SetConfiguration(uint8); + int SetAltSetting(uint8, uint8); + int ClearHalt(uint8); + status_t SubmitTransfer(struct usbi_transfer *); + status_t CancelTransfer(USBTransfer *); + bool InitCheck(); +private: + int fRawFD; + static status_t TransfersThread(void *); + void TransfersWorker(); + USBDevice* fUSBDevice; + unsigned int fClaimedInterfaces; + BList fTransfers; + BLocker fTransfersLock; + sem_id fTransfersSem; + thread_id fTransfersThread; + bool fInitCheck; +}; + +class USBTransfer { +public: + USBTransfer(struct usbi_transfer *, USBDevice *); + virtual ~USBTransfer(); + void Do(int); + struct usbi_transfer* UsbiTransfer(); + void SetCancelled(); + bool IsCancelled(); +private: + struct usbi_transfer* fUsbiTransfer; + struct libusb_transfer* fLibusbTransfer; + USBDevice* fUSBDevice; + BLocker fStatusLock; + bool fCancelled; +}; + +class USBRoster { +public: + USBRoster(); + virtual ~USBRoster(); + int Start(); + void Stop(); +private: + void* fLooper; +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_backend.cpp b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_backend.cpp new file mode 100644 index 00000000..2fcefdd8 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_backend.cpp @@ -0,0 +1,532 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include + +#include "haiku_usb.h" + +static int _errno_to_libusb(int status) +{ + return status; +} + +USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device) +{ + fUsbiTransfer = itransfer; + fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + fUSBDevice = device; + fCancelled = false; +} + +USBTransfer::~USBTransfer() +{ +} + +struct usbi_transfer * +USBTransfer::UsbiTransfer() +{ + return fUsbiTransfer; +} + +void +USBTransfer::SetCancelled() +{ + fCancelled = true; +} + +bool +USBTransfer::IsCancelled() +{ + return fCancelled; +} + +void +USBTransfer::Do(int fRawFD) +{ + switch (fLibusbTransfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + { + struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer; + usb_raw_command command; + command.control.request_type = setup->bmRequestType; + command.control.request = setup->bRequest; + command.control.value = setup->wValue; + command.control.index = setup->wIndex; + command.control.length = setup->wLength; + command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + if (fCancelled) + break; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || + command.control.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer"); + break; + } + fUsbiTransfer->transferred = command.control.length; + } + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + { + usb_raw_command command; + command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); + command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); + command.transfer.data = fLibusbTransfer->buffer; + command.transfer.length = fLibusbTransfer->length; + if (fCancelled) + break; + if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) { + if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) || + command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer"); + break; + } + } + else { + if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) || + command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer"); + break; + } + } + fUsbiTransfer->transferred = command.transfer.length; + } + break; + // IsochronousTransfers not tested + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + { + usb_raw_command command; + command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); + command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); + command.isochronous.data = fLibusbTransfer->buffer; + command.isochronous.length = fLibusbTransfer->length; + command.isochronous.packet_count = fLibusbTransfer->num_iso_packets; + int i; + usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets]; + for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { + if ((fLibusbTransfer->iso_packet_desc[i]).length > (unsigned int)INT16_MAX) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); + break; + } + packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length; + } + if (i < fLibusbTransfer->num_iso_packets) + break; // TODO Handle this error + command.isochronous.packet_descriptors = packetDescriptors; + if (fCancelled) + break; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) || + command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); + break; + } + for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { + (fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length; + switch (packetDescriptors[i].status) { + case B_OK: + (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED; + break; + default: + (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR; + break; + } + } + delete[] packetDescriptors; + // Do we put the length of transfer here, for isochronous transfers? + fUsbiTransfer->transferred = command.transfer.length; + } + break; + default: + usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer"); + } +} + +bool +USBDeviceHandle::InitCheck() +{ + return fInitCheck; +} + +status_t +USBDeviceHandle::TransfersThread(void *self) +{ + USBDeviceHandle *handle = (USBDeviceHandle *)self; + handle->TransfersWorker(); + return B_OK; +} + +void +USBDeviceHandle::TransfersWorker() +{ + while (true) { + status_t status = acquire_sem(fTransfersSem); + if (status == B_BAD_SEM_ID) + break; + if (status == B_INTERRUPTED) + continue; + fTransfersLock.Lock(); + USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0); + fTransfersLock.Unlock(); + fPendingTransfer->Do(fRawFD); + usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer()); + } +} + +status_t +USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer) +{ + USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice); + *((USBTransfer **)usbi_get_transfer_priv(itransfer)) = transfer; + BAutolock locker(fTransfersLock); + fTransfers.AddItem(transfer); + release_sem(fTransfersSem); + return LIBUSB_SUCCESS; +} + +status_t +USBDeviceHandle::CancelTransfer(USBTransfer *transfer) +{ + transfer->SetCancelled(); + fTransfersLock.Lock(); + bool removed = fTransfers.RemoveItem(transfer); + fTransfersLock.Unlock(); + if (removed) + usbi_signal_transfer_completion(transfer->UsbiTransfer()); + return LIBUSB_SUCCESS; +} + +USBDeviceHandle::USBDeviceHandle(USBDevice *dev) + : + fUSBDevice(dev), + fClaimedInterfaces(0), + fTransfersThread(-1), + fInitCheck(false) +{ + fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC); + if (fRawFD < 0) { + usbi_err(NULL,"failed to open device"); + return; + } + fTransfersSem = create_sem(0, "Transfers Queue Sem"); + fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this); + resume_thread(fTransfersThread); + fInitCheck = true; +} + +USBDeviceHandle::~USBDeviceHandle() +{ + if (fRawFD > 0) + close(fRawFD); + for (int i = 0; i < 32; i++) { + if (fClaimedInterfaces & (1U << i)) + ReleaseInterface(i); + } + delete_sem(fTransfersSem); + if (fTransfersThread > 0) + wait_for_thread(fTransfersThread, NULL); +} + +int +USBDeviceHandle::ClaimInterface(uint8 inumber) +{ + int status = fUSBDevice->ClaimInterface(inumber); + if (status == LIBUSB_SUCCESS) + fClaimedInterfaces |= (1U << inumber); + return status; +} + +int +USBDeviceHandle::ReleaseInterface(uint8 inumber) +{ + fUSBDevice->ReleaseInterface(inumber); + fClaimedInterfaces &= ~(1U << inumber); + return LIBUSB_SUCCESS; +} + +int +USBDeviceHandle::SetConfiguration(uint8 config) +{ + int config_index = fUSBDevice->CheckInterfacesFree(config); + if (config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND) + return config_index; + usb_raw_command command; + command.config.config_index = config_index; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + return _errno_to_libusb(command.config.status); + } + fUSBDevice->SetActiveConfiguration((uint8)config_index); + return LIBUSB_SUCCESS; +} + +int +USBDeviceHandle::SetAltSetting(uint8 inumber, uint8 alt) +{ + usb_raw_command command; + command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex(); + command.alternate.interface_index = inumber; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) || + command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "Error retrieving active alternate interface"); + return _errno_to_libusb(command.alternate.status); + } + if (command.alternate.alternate_info == (uint32)alt) { + 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 DISCONNECTED PROBABLY + usbi_err(NULL, "Error setting alternate interface"); + return _errno_to_libusb(command.alternate.status); + } + usbi_dbg(NULL, "Setting alternate interface successful"); + return LIBUSB_SUCCESS; +} + +int +USBDeviceHandle::ClearHalt(uint8 endpoint) +{ + usb_raw_command command; + command.control.request_type = USB_REQTYPE_ENDPOINT_OUT; + command.control.request = USB_REQUEST_CLEAR_FEATURE; + command.control.value = USB_FEATURE_ENDPOINT_HALT; + command.control.index = endpoint; + command.control.length = 0; + + if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || + command.control.status != B_USB_RAW_STATUS_SUCCESS) { + return _errno_to_libusb(command.control.status); + } + return LIBUSB_SUCCESS; +} + + +USBDevice::USBDevice(const char *path) + : + fClaimedInterfaces(0), + fConfigurationDescriptors(NULL), + fActiveConfiguration(0), //0? + fPath(NULL), + fEndpointToIndex(NULL), + fEndpointToInterface(NULL), + fInitCheck(false) +{ + fPath=strdup(path); + Initialise(); +} + +USBDevice::~USBDevice() +{ + free(fPath); + if (fConfigurationDescriptors) { + for (uint8 i = 0; i < fDeviceDescriptor.num_configurations; i++) { + if (fConfigurationDescriptors[i]) + delete fConfigurationDescriptors[i]; + } + delete[] fConfigurationDescriptors; + } + if (fEndpointToIndex) + delete[] fEndpointToIndex; + if (fEndpointToInterface) + delete[] fEndpointToInterface; +} + +bool +USBDevice::InitCheck() +{ + return fInitCheck; +} + +const char * +USBDevice::Location() const +{ + return fPath; +} + +uint8 +USBDevice::CountConfigurations() const +{ + return fDeviceDescriptor.num_configurations; +} + +const usb_device_descriptor * +USBDevice::Descriptor() const +{ + return &fDeviceDescriptor; +} + +const usb_configuration_descriptor * +USBDevice::ConfigurationDescriptor(uint8 index) const +{ + if (index > CountConfigurations()) + return NULL; + return (usb_configuration_descriptor *) fConfigurationDescriptors[index]; +} + +const usb_configuration_descriptor * +USBDevice::ActiveConfiguration() const +{ + return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration]; +} + +uint8 +USBDevice::ActiveConfigurationIndex() const +{ + return fActiveConfiguration; +} + +int USBDevice::ClaimInterface(uint8 interface) +{ + if (interface > ActiveConfiguration()->number_interfaces) + return LIBUSB_ERROR_NOT_FOUND; + if (fClaimedInterfaces & (1U << interface)) + return LIBUSB_ERROR_BUSY; + fClaimedInterfaces |= (1U << interface); + return LIBUSB_SUCCESS; +} + +int USBDevice::ReleaseInterface(uint8 interface) +{ + fClaimedInterfaces &= ~(1U << interface); + return LIBUSB_SUCCESS; +} + +int +USBDevice::CheckInterfacesFree(uint8 config) +{ + if (fConfigToIndex.count(config) == 0) + return LIBUSB_ERROR_NOT_FOUND; + if (fClaimedInterfaces == 0) + return fConfigToIndex[config]; + return LIBUSB_ERROR_BUSY; +} + +void +USBDevice::SetActiveConfiguration(uint8 config_index) +{ + fActiveConfiguration = config_index; +} + +uint8 +USBDevice::EndpointToIndex(uint8 address) const +{ + return fEndpointToIndex[fActiveConfiguration][address]; +} + +uint8 +USBDevice::EndpointToInterface(uint8 address) const +{ + return fEndpointToInterface[fActiveConfiguration][address]; +} + +int +USBDevice::Initialise() //Do we need more error checking, etc? How to report? +{ + int fRawFD = open(fPath, O_RDWR | O_CLOEXEC); + if (fRawFD < 0) + return B_ERROR; + usb_raw_command command; + command.device.descriptor = &fDeviceDescriptor; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) || + command.device.status != B_USB_RAW_STATUS_SUCCESS) { + close(fRawFD); + return B_ERROR; + } + + fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations]; + fEndpointToIndex = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; + fEndpointToInterface = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; + for (uint8 i = 0; i < fDeviceDescriptor.num_configurations; i++) { + usb_configuration_descriptor tmp_config; + command.config.descriptor = &tmp_config; + command.config.config_index = i; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving configuration descriptor"); + close(fRawFD); + return B_ERROR; + } + fConfigToIndex[tmp_config.configuration_value] = i; + fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length]; + + command.config_etc.descriptor = (usb_configuration_descriptor*)fConfigurationDescriptors[i]; + command.config_etc.length = tmp_config.total_length; + command.config_etc.config_index = i; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC, &command, sizeof(command)) || + command.config_etc.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving full configuration descriptor"); + close(fRawFD); + return B_ERROR; + } + + for (uint8 j = 0; j < tmp_config.number_interfaces; j++) { + command.alternate.config_index = i; + command.alternate.interface_index = j; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving number of alternate interfaces"); + close(fRawFD); + return B_ERROR; + } + uint8 num_alternate = (uint8)command.alternate.alternate_info; + for (uint8 k = 0; k < num_alternate; k++) { + usb_interface_descriptor tmp_interface; + command.interface_etc.config_index = i; + command.interface_etc.interface_index = j; + command.interface_etc.alternate_index = k; + command.interface_etc.descriptor = &tmp_interface; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving interface descriptor"); + close(fRawFD); + return B_ERROR; + } + for (uint8 l = 0; l < tmp_interface.num_endpoints; l++) { + usb_endpoint_descriptor tmp_endpoint; + command.endpoint_etc.config_index = i; + command.endpoint_etc.interface_index = j; + command.endpoint_etc.alternate_index = k; + command.endpoint_etc.endpoint_index = l; + command.endpoint_etc.descriptor = &tmp_endpoint; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving endpoint descriptor"); + close(fRawFD); + return B_ERROR; + } + fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l; + fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j; + } + } + } + } + close(fRawFD); + fInitCheck = true; + return B_OK; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.cpp b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.cpp new file mode 100644 index 00000000..620b5b53 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.cpp @@ -0,0 +1,235 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include + +#include "haiku_usb.h" + +USBRoster gUsbRoster; +int32 gInitCount = 0; + +static int haiku_get_config_descriptor(struct libusb_device *, uint8_t, + void *, size_t); + +static int +haiku_init(struct libusb_context *ctx) +{ + UNUSED(ctx); + if (atomic_add(&gInitCount, 1) == 0) + return gUsbRoster.Start(); + return LIBUSB_SUCCESS; +} + +static void +haiku_exit(struct libusb_context *ctx) +{ + UNUSED(ctx); + if (atomic_add(&gInitCount, -1) == 1) + gUsbRoster.Stop(); +} + +static int +haiku_open(struct libusb_device_handle *dev_handle) +{ + USBDevice *dev = *((USBDevice **)usbi_get_device_priv(dev_handle->dev)); + USBDeviceHandle *handle = new(std::nothrow) USBDeviceHandle(dev); + if (handle == NULL) + return LIBUSB_ERROR_NO_MEM; + if (handle->InitCheck() == false) { + delete handle; + return LIBUSB_ERROR_NO_DEVICE; + } + *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)) = handle; + return LIBUSB_SUCCESS; +} + +static void +haiku_close(struct libusb_device_handle *dev_handle) +{ + USBDeviceHandle **pHandle = (USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle); + USBDeviceHandle *handle = *pHandle; + if (handle == NULL) + return; + delete handle; + *pHandle = NULL; +} + +static int +haiku_get_active_config_descriptor(struct libusb_device *device, void *buffer, size_t len) +{ + USBDevice *dev = *((USBDevice **)usbi_get_device_priv(device)); + return haiku_get_config_descriptor(device, dev->ActiveConfigurationIndex(), buffer, len); +} + +static int +haiku_get_config_descriptor(struct libusb_device *device, uint8_t config_index, void *buffer, size_t len) +{ + USBDevice *dev = *((USBDevice **)usbi_get_device_priv(device)); + const usb_configuration_descriptor *config = dev->ConfigurationDescriptor(config_index); + if (config == NULL) { + usbi_err(DEVICE_CTX(device), "failed getting configuration descriptor"); + return LIBUSB_ERROR_IO; + } + if (len > config->total_length) { + len = config->total_length; + } + memcpy(buffer, config, len); + return len; +} + +static int +haiku_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + USBDeviceHandle *handle= *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)); + if (config <= 0) + return LIBUSB_ERROR_NOT_SUPPORTED; // cannot unconfigure + return handle->SetConfiguration((uint8)config); +} + +static int +haiku_claim_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)); + return handle->ClaimInterface(interface_number); +} + +static int +haiku_set_altsetting(struct libusb_device_handle *dev_handle, uint8_t interface_number, uint8_t altsetting) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)); + return handle->SetAltSetting(interface_number, altsetting); +} + +static int +haiku_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)); + return handle->ClearHalt(endpoint); +} + +static int +haiku_release_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)usbi_get_device_handle_priv(dev_handle)); + haiku_set_altsetting(dev_handle, interface_number, 0); + return handle->ReleaseInterface(interface_number); +} + +static int +haiku_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)usbi_get_device_handle_priv(fLibusbTransfer->dev_handle)); + return fDeviceHandle->SubmitTransfer(itransfer); +} + +static int +haiku_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)usbi_get_device_handle_priv(fLibusbTransfer->dev_handle)); + return fDeviceHandle->CancelTransfer(*((USBTransfer **)usbi_get_transfer_priv(itransfer))); +} + +static int +haiku_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + USBTransfer **pTransfer = (USBTransfer **)usbi_get_transfer_priv(itransfer); + USBTransfer *transfer = *pTransfer; + + usbi_mutex_lock(&itransfer->lock); + if (transfer->IsCancelled()) { + delete transfer; + *pTransfer = NULL; + usbi_mutex_unlock(&itransfer->lock); + if (itransfer->transferred < 0) + itransfer->transferred = 0; + return usbi_handle_transfer_cancellation(itransfer); + } + libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; + if (itransfer->transferred < 0) { + usbi_err(ITRANSFER_CTX(itransfer), "error in transfer"); + status = LIBUSB_TRANSFER_ERROR; + itransfer->transferred = 0; + } + delete transfer; + *pTransfer = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); +} + +const struct usbi_os_backend usbi_backend = { + /*.name =*/ "Haiku usbfs", + /*.caps =*/ 0, + /*.init =*/ haiku_init, + /*.exit =*/ haiku_exit, + /*.set_option =*/ NULL, + /*.get_device_list =*/ NULL, + /*.get_device_string =*/ NULL, + /*.hotplug_poll =*/ NULL, + /*.wrap_sys_device =*/ NULL, + /*.open =*/ haiku_open, + /*.close =*/ haiku_close, + + /*.get_active_config_descriptor =*/ haiku_get_active_config_descriptor, + /*.get_config_descriptor =*/ haiku_get_config_descriptor, + /*.get_config_descriptor_by_value =*/ NULL, + + /*.get_configuration =*/ NULL, + /*.set_configuration =*/ haiku_set_configuration, + + /*.claim_interface =*/ haiku_claim_interface, + /*.release_interface =*/ haiku_release_interface, + /*.set_interface_altsetting =*/ haiku_set_altsetting, + + /*.clear_halt =*/ haiku_clear_halt, + /*.reset_device =*/ NULL, + + /*.alloc_streams =*/ NULL, + /*.free_streams =*/ NULL, + + /*.dev_mem_alloc =*/ NULL, + /*.dev_mem_free =*/ NULL, + + /*.kernel_driver_active =*/ NULL, + /*.detach_kernel_driver =*/ NULL, + /*.attach_kernel_driver =*/ NULL, + /*.endpoint_supports_raw_io =*/ NULL, + /*.endpoint_set_raw_io =*/ NULL, + /*.get_max_raw_io_transfer_size =*/ NULL, + + /*.destroy_device =*/ NULL, + + /*.submit_transfer =*/ haiku_submit_transfer, + /*.cancel_transfer =*/ haiku_cancel_transfer, + /*.clear_transfer_priv =*/ NULL, + + /*.handle_events =*/ NULL, + /*.handle_transfer_completion =*/ haiku_handle_transfer_completion, + + /*.context_priv_size =*/ 0, + /*.device_priv_size =*/ sizeof(USBDevice *), + /*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *), + /*.transfer_priv_size =*/ sizeof(USBTransfer *), +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.h new file mode 100644 index 00000000..d371f018 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/haiku_usb_raw.h @@ -0,0 +1,188 @@ +/* + * Copyright 2006-2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef _USB_RAW_H_ +#define _USB_RAW_H_ + +#include + +#define B_USB_RAW_PROTOCOL_VERSION 0x0015 +#define B_USB_RAW_ACTIVE_ALTERNATE 0xffffffff + +typedef enum { + B_USB_RAW_COMMAND_GET_VERSION = 0x1000, + + B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR = 0x2000, + B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_STRING_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, + B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, + B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, + B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, + B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR_ETC, + B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC, + + B_USB_RAW_COMMAND_SET_CONFIGURATION = 0x3000, + B_USB_RAW_COMMAND_SET_FEATURE, + B_USB_RAW_COMMAND_CLEAR_FEATURE, + B_USB_RAW_COMMAND_GET_STATUS, + B_USB_RAW_COMMAND_GET_DESCRIPTOR, + B_USB_RAW_COMMAND_SET_ALT_INTERFACE, + + B_USB_RAW_COMMAND_CONTROL_TRANSFER = 0x4000, + B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, + B_USB_RAW_COMMAND_BULK_TRANSFER, + B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER +} usb_raw_command_id; + + +typedef enum { + B_USB_RAW_STATUS_SUCCESS = 0, + + B_USB_RAW_STATUS_FAILED, + B_USB_RAW_STATUS_ABORTED, + B_USB_RAW_STATUS_STALLED, + B_USB_RAW_STATUS_CRC_ERROR, + B_USB_RAW_STATUS_TIMEOUT, + + B_USB_RAW_STATUS_INVALID_CONFIGURATION, + B_USB_RAW_STATUS_INVALID_INTERFACE, + B_USB_RAW_STATUS_INVALID_ENDPOINT, + B_USB_RAW_STATUS_INVALID_STRING, + + B_USB_RAW_STATUS_NO_MEMORY +} usb_raw_command_status; + + +typedef union { + struct { + status_t status; + } version; + + struct { + status_t status; + usb_device_descriptor *descriptor; + } device; + + struct { + status_t status; + usb_configuration_descriptor *descriptor; + uint32 config_index; + } config; + + struct { + status_t status; + usb_configuration_descriptor *descriptor; + uint32 config_index; + size_t length; + } config_etc; + + struct { + status_t status; + uint32 alternate_info; + uint32 config_index; + uint32 interface_index; + } alternate; + + struct { + status_t status; + usb_interface_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + } interface; + + struct { + status_t status; + usb_interface_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + } interface_etc; + + struct { + status_t status; + usb_endpoint_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 endpoint_index; + } endpoint; + + struct { + status_t status; + usb_endpoint_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + uint32 endpoint_index; + } endpoint_etc; + + struct { + status_t status; + usb_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 generic_index; + size_t length; + } generic; + + struct { + status_t status; + usb_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + uint32 generic_index; + size_t length; + } generic_etc; + + struct { + status_t status; + usb_string_descriptor *descriptor; + uint32 string_index; + size_t length; + } string; + + struct { + status_t status; + uint8 type; + uint8 index; + uint16 language_id; + void *data; + size_t length; + } descriptor; + + struct { + status_t status; + uint8 request_type; + uint8 request; + uint16 value; + uint16 index; + uint16 length; + void *data; + } control; + + struct { + status_t status; + uint32 interface; + uint32 endpoint; + void *data; + size_t length; + } transfer; + + struct { + status_t status; + uint32 interface; + uint32 endpoint; + void *data; + size_t length; + usb_iso_packet_descriptor *packet_descriptors; + uint32 packet_count; + } isochronous; +} usb_raw_command; + +#endif // _USB_RAW_H_ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_netlink.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_netlink.c new file mode 100644 index 00000000..899084f2 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_netlink.c @@ -0,0 +1,401 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * Copyright (c) 2013 Nathan Hjelm + * Copyright (c) 2016 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" +#include "linux_usbfs.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ASM_TYPES_H +#include +#endif +#include +#include + +#define NL_GROUP_KERNEL 1 + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK 0 +#endif + +static int linux_netlink_socket = -1; +static usbi_event_t netlink_control_event = USBI_INVALID_EVENT; +static pthread_t libusb_linux_event_thread; + +static void *linux_netlink_event_thread_main(void *arg); + +static int set_fd_cloexec_nb(int fd, int socktype) +{ + int flags; + +#if defined(FD_CLOEXEC) + /* Make sure the netlink socket file descriptor is marked as CLOEXEC */ + if (!(socktype & SOCK_CLOEXEC)) { + flags = fcntl(fd, F_GETFD); + if (flags == -1) { + usbi_err(NULL, "failed to get netlink fd flags, errno=%d", errno); + return -1; + } + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + usbi_err(NULL, "failed to set netlink fd flags, errno=%d", errno); + return -1; + } + } +#endif + + /* Make sure the netlink socket is non-blocking */ + if (!(socktype & SOCK_NONBLOCK)) { + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + usbi_err(NULL, "failed to get netlink fd status flags, errno=%d", errno); + return -1; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + usbi_err(NULL, "failed to set netlink fd status flags, errno=%d", errno); + return -1; + } + } + + return 0; +} + +int linux_netlink_start_event_monitor(void) +{ + struct sockaddr_nl sa_nl = { .nl_family = AF_NETLINK, .nl_groups = NL_GROUP_KERNEL }; + int socktype = SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC; + int opt = 1; + int ret; + + linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT); + if (linux_netlink_socket == -1 && errno == EINVAL) { + 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); + } + + if (linux_netlink_socket == -1) { + usbi_err(NULL, "failed to create netlink socket, errno=%d", errno); + goto err; + } + + ret = set_fd_cloexec_nb(linux_netlink_socket, socktype); + if (ret == -1) + goto err_close_socket; + + ret = bind(linux_netlink_socket, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); + if (ret == -1) { + usbi_err(NULL, "failed to bind netlink socket, errno=%d", errno); + goto err_close_socket; + } + + ret = setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); + if (ret == -1) { + usbi_err(NULL, "failed to set netlink socket SO_PASSCRED option, errno=%d", errno); + goto err_close_socket; + } + + ret = usbi_create_event(&netlink_control_event); + if (ret) { + usbi_err(NULL, "failed to create netlink control event"); + goto err_close_socket; + } + + ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL); + if (ret != 0) { + usbi_err(NULL, "failed to create netlink event thread (%d)", ret); + goto err_destroy_event; + } + + return LIBUSB_SUCCESS; + +err_destroy_event: + usbi_destroy_event(&netlink_control_event); + netlink_control_event = (usbi_event_t)USBI_INVALID_EVENT; +err_close_socket: + close(linux_netlink_socket); + linux_netlink_socket = -1; +err: + return LIBUSB_ERROR_OTHER; +} + +int linux_netlink_stop_event_monitor(void) +{ + int ret; + + assert(linux_netlink_socket != -1); + + /* Signal the control event and wait for the thread to exit */ + usbi_signal_event(&netlink_control_event); + + ret = pthread_join(libusb_linux_event_thread, NULL); + if (ret) + usbi_warn(NULL, "failed to join netlink event thread (%d)", ret); + + usbi_destroy_event(&netlink_control_event); + netlink_control_event = (usbi_event_t)USBI_INVALID_EVENT; + + close(linux_netlink_socket); + linux_netlink_socket = -1; + + return LIBUSB_SUCCESS; +} + +static const char *netlink_message_parse(const char *buffer, size_t len, const char *key) +{ + const char *end = buffer + len; + size_t keylen = strlen(key); + + while (buffer < end && *buffer) { + if (strncmp(buffer, key, keylen) == 0 && buffer[keylen] == '=') + return buffer + keylen + 1; + buffer += strlen(buffer) + 1; + } + + return NULL; +} + +/* parse parts of netlink message common to both libudev and the kernel */ +static int linux_netlink_parse(const char *buffer, size_t len, int *detached, + const char **sys_name, uint8_t *busnum, uint8_t *devaddr) +{ + const char *tmp, *slash; + + errno = 0; + + *sys_name = NULL; + *detached = 0; + *busnum = 0; + *devaddr = 0; + + tmp = netlink_message_parse(buffer, len, "ACTION"); + if (!tmp) { + return -1; + } else if (strcmp(tmp, "remove") == 0) { + *detached = 1; + } else if (strcmp(tmp, "add") != 0) { + usbi_dbg(NULL, "unknown device action %s", tmp); + return -1; + } + + /* check that this is a usb message */ + tmp = netlink_message_parse(buffer, len, "SUBSYSTEM"); + if (!tmp || strcmp(tmp, "usb") != 0) { + /* not usb. ignore */ + return -1; + } + + /* check that this is an actual usb device */ + tmp = netlink_message_parse(buffer, len, "DEVTYPE"); + if (!tmp || strcmp(tmp, "usb_device") != 0) { + /* not usb. ignore */ + return -1; + } + + tmp = netlink_message_parse(buffer, len, "BUSNUM"); + if (tmp) { + *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + tmp = netlink_message_parse(buffer, len, "DEVNUM"); + if (NULL == tmp) + return -1; + + *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + } else { + /* no bus number. try "DEVICE" */ + tmp = netlink_message_parse(buffer, len, "DEVICE"); + if (!tmp) { + /* not usb. ignore */ + return -1; + } + + /* Parse a device path such as /dev/bus/usb/003/004 */ + slash = strrchr(tmp, '/'); + if (!slash) + return -1; + + *busnum = (uint8_t)(strtoul(slash - 3, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + *devaddr = (uint8_t)(strtoul(slash + 1, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + return 0; + } + + tmp = netlink_message_parse(buffer, len, "DEVPATH"); + if (!tmp) + return -1; + + slash = strrchr(tmp, '/'); + if (slash) + *sys_name = slash + 1; + + /* found a usb device */ + return 0; +} + +static int linux_netlink_read_message(void) +{ + char cred_buffer[CMSG_SPACE(sizeof(struct ucred))]; + char msg_buffer[2048]; + const char *sys_name = NULL; + uint8_t busnum, devaddr; + int detached, r; + ssize_t len; + struct cmsghdr *cmsg; + struct ucred *cred; + struct sockaddr_nl sa_nl; + struct iovec iov = { .iov_base = msg_buffer, .iov_len = sizeof(msg_buffer) }; + struct msghdr msg = { + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer), + .msg_name = &sa_nl, .msg_namelen = sizeof(sa_nl) + }; + + /* read netlink message */ + len = recvmsg(linux_netlink_socket, &msg, 0); + if (len == -1) { + if (errno != EAGAIN && errno != EINTR) + usbi_err(NULL, "error receiving message from netlink, errno=%d", errno); + return -1; + } + + if (len < 32 || (msg.msg_flags & MSG_TRUNC)) { + usbi_err(NULL, "invalid netlink message length"); + return -1; + } + + if (sa_nl.nl_groups != NL_GROUP_KERNEL || sa_nl.nl_pid != 0) { + 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(NULL, "ignoring netlink message with no sender credentials"); + return -1; + } + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + usbi_dbg(NULL, "ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid); + return -1; + } + + r = linux_netlink_parse(msg_buffer, (size_t)len, &detached, &sys_name, &busnum, &devaddr); + if (r) + return r; + + 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 */ + if (detached) + linux_device_disconnected(busnum, devaddr); + else + linux_hotplug_enumerate(busnum, devaddr, sys_name); + + return 0; +} + +static void *linux_netlink_event_thread_main(void *arg) +{ + struct pollfd fds[] = { + { .fd = USBI_EVENT_OS_HANDLE(&netlink_control_event), + .events = USBI_EVENT_POLL_EVENTS }, + { .fd = linux_netlink_socket, + .events = POLLIN }, + }; + int r; + + UNUSED(arg); + +#if defined(HAVE_PTHREAD_SETNAME_NP) + r = pthread_setname_np(pthread_self(), "libusb_event"); + if (r) + usbi_warn(NULL, "failed to set hotplug event thread name, error=%d", r); +#endif + + usbi_dbg(NULL, "netlink event thread entering"); + + while (1) { + r = poll(fds, 2, -1); + if (r == -1) { + /* check for temporary failure */ + if (errno == EINTR) + continue; + usbi_err(NULL, "poll() failed, errno=%d", errno); + break; + } + if (fds[0].revents) { + /* activity on control event, exit */ + break; + } + if (fds[1].revents) { + usbi_mutex_static_lock(&linux_hotplug_lock); + linux_netlink_read_message(); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + } + + usbi_dbg(NULL, "netlink event thread exiting"); + + return NULL; +} + +void linux_netlink_hotplug_poll(void) +{ + int r; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + r = linux_netlink_read_message(); + } while (r == 0); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_udev.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_udev.c new file mode 100644 index 00000000..9ec9eb17 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_udev.c @@ -0,0 +1,321 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * Copyright (c) 2012-2013 Nathan Hjelm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" +#include "linux_usbfs.h" + +#include +#include +#include +#include +#include +#include +#include + +/* udev context */ +static struct udev *udev_ctx = NULL; +static int udev_monitor_fd = -1; +static usbi_event_t udev_control_event = USBI_INVALID_EVENT; +static struct udev_monitor *udev_monitor = NULL; +static pthread_t linux_event_thread; + +static void udev_hotplug_event(struct udev_device *udev_dev); +static void *linux_udev_event_thread_main(void *arg); + +int linux_udev_start_event_monitor(void) +{ + int r; + + assert(udev_ctx == NULL); + udev_ctx = udev_new(); + if (!udev_ctx) { + usbi_err(NULL, "could not create udev context"); + goto err; + } + + udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); + if (!udev_monitor) { + usbi_err(NULL, "could not initialize udev monitor"); + goto err_free_ctx; + } + + r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device"); + if (r) { + usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem"); + goto err_free_monitor; + } + + if (udev_monitor_enable_receiving(udev_monitor)) { + usbi_err(NULL, "failed to enable the udev monitor"); + goto err_free_monitor; + } + + udev_monitor_fd = udev_monitor_get_fd(udev_monitor); + +#if defined(FD_CLOEXEC) + /* Make sure the udev file descriptor is marked as CLOEXEC */ + r = fcntl(udev_monitor_fd, F_GETFD); + if (r == -1) { + usbi_err(NULL, "failed to get udev monitor fd flags, errno=%d", errno); + goto err_free_monitor; + } + if (!(r & FD_CLOEXEC)) { + if (fcntl(udev_monitor_fd, F_SETFD, r | FD_CLOEXEC) == -1) { + usbi_err(NULL, "failed to set udev monitor fd flags, errno=%d", errno); + goto err_free_monitor; + } + } +#endif + + /* Some older versions of udev are not non-blocking by default, + * so make sure this is set */ + r = fcntl(udev_monitor_fd, F_GETFL); + if (r == -1) { + usbi_err(NULL, "failed to get udev monitor fd status flags, errno=%d", errno); + goto err_free_monitor; + } + if (!(r & O_NONBLOCK)) { + if (fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK) == -1) { + usbi_err(NULL, "failed to set udev monitor fd status flags, errno=%d", errno); + goto err_free_monitor; + } + } + + r = usbi_create_event(&udev_control_event); + if (r) { + usbi_err(NULL, "failed to create udev control event"); + goto err_free_monitor; + } + + r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL); + if (r) { + usbi_err(NULL, "failed to create hotplug event thread (%d)", r); + goto err_destroy_event; + } + + return LIBUSB_SUCCESS; + +err_destroy_event: + usbi_destroy_event(&udev_control_event); + udev_control_event = (usbi_event_t)USBI_INVALID_EVENT; +err_free_monitor: + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_monitor_fd = -1; +err_free_ctx: + udev_unref(udev_ctx); +err: + udev_ctx = NULL; + return LIBUSB_ERROR_OTHER; +} + +int linux_udev_stop_event_monitor(void) +{ + int r; + + assert(udev_ctx != NULL); + assert(udev_monitor != NULL); + assert(udev_monitor_fd != -1); + + /* Signal the control event and wait for the thread to exit */ + usbi_signal_event(&udev_control_event); + + r = pthread_join(linux_event_thread, NULL); + if (r) + usbi_warn(NULL, "failed to join hotplug event thread (%d)", r); + + usbi_destroy_event(&udev_control_event); + udev_control_event = (usbi_event_t)USBI_INVALID_EVENT; + + /* Release the udev monitor */ + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_monitor_fd = -1; + + /* Clean up the udev context */ + udev_unref(udev_ctx); + udev_ctx = NULL; + + return LIBUSB_SUCCESS; +} + +static void *linux_udev_event_thread_main(void *arg) +{ + struct pollfd fds[] = { + { .fd = USBI_EVENT_OS_HANDLE(&udev_control_event), + .events = USBI_EVENT_POLL_EVENTS }, + { .fd = udev_monitor_fd, + .events = POLLIN }, + }; + struct udev_device *udev_dev; + int r; + + UNUSED(arg); + +#if defined(HAVE_PTHREAD_SETNAME_NP) + r = pthread_setname_np(pthread_self(), "libusb_event"); + if (r) + usbi_warn(NULL, "failed to set hotplug event thread name, error=%d", r); +#endif + + usbi_dbg(NULL, "udev event thread entering"); + + while (1) { + r = poll(fds, 2, -1); + if (r == -1) { + /* check for temporary failure */ + if (errno == EINTR) + continue; + usbi_err(NULL, "poll() failed, errno=%d", errno); + break; + } + if (fds[0].revents) { + /* activity on control event, exit */ + break; + } + if (fds[1].revents) { + usbi_mutex_static_lock(&linux_hotplug_lock); + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) + udev_hotplug_event(udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + } + + usbi_dbg(NULL, "udev event thread exiting"); + + return NULL; +} + +static int udev_device_info(struct libusb_context *ctx, int detached, + struct udev_device *udev_dev, uint8_t *busnum, + uint8_t *devaddr, const char **sys_name) { + const char *dev_node; + + dev_node = udev_device_get_devnode(udev_dev); + if (!dev_node) { + return LIBUSB_ERROR_OTHER; + } + + *sys_name = udev_device_get_sysname(udev_dev); + if (!*sys_name) { + return LIBUSB_ERROR_OTHER; + } + + return linux_get_device_address(ctx, detached, busnum, devaddr, + dev_node, *sys_name, -1); +} + +static void udev_hotplug_event(struct udev_device *udev_dev) +{ + const char *udev_action; + const char *sys_name = NULL; + uint8_t busnum = 0, devaddr = 0; + int detached; + int r; + + do { + udev_action = udev_device_get_action(udev_dev); + if (!udev_action) { + break; + } + + detached = !strncmp(udev_action, "remove", 6); + + r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name); + if (LIBUSB_SUCCESS != r) { + break; + } + + usbi_dbg(NULL, "udev hotplug event. action: %s.", udev_action); + + if (strncmp(udev_action, "add", 3) == 0) { + linux_hotplug_enumerate(busnum, devaddr, sys_name); + } else if (detached) { + linux_device_disconnected(busnum, devaddr); + } else if (strncmp(udev_action, "bind", 4) == 0) { + /* silently ignore "known unhandled" action */ + } else { + usbi_err(NULL, "ignoring udev action %s", udev_action); + } + } while (0); + + udev_device_unref(udev_dev); +} + +int linux_udev_scan_devices(struct libusb_context *ctx) +{ + struct udev_enumerate *enumerator; + struct udev_list_entry *devices, *entry; + struct udev_device *udev_dev; + const char *sys_name; + int r; + + assert(udev_ctx != NULL); + + enumerator = udev_enumerate_new(udev_ctx); + if (NULL == enumerator) { + usbi_err(ctx, "error creating udev enumerator"); + return LIBUSB_ERROR_OTHER; + } + + udev_enumerate_add_match_subsystem(enumerator, "usb"); + udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); + udev_enumerate_scan_devices(enumerator); + devices = udev_enumerate_get_list_entry(enumerator); + + entry = NULL; + udev_list_entry_foreach(entry, devices) { + const char *path = udev_list_entry_get_name(entry); + uint8_t busnum = 0, devaddr = 0; + + udev_dev = udev_device_new_from_syspath(udev_ctx, path); + + r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name); + if (r) { + udev_device_unref(udev_dev); + continue; + } + + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + udev_device_unref(udev_dev); + } + + udev_enumerate_unref(enumerator); + + return LIBUSB_SUCCESS; +} + +void linux_udev_hotplug_poll(void) +{ + struct udev_device *udev_dev; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) { + usbi_dbg(NULL, "Handling hotplug event from hotplug_poll"); + udev_hotplug_event(udev_dev); + } + } while (udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.c new file mode 100644 index 00000000..74e73f9a --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.c @@ -0,0 +1,2877 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2013 Nathan Hjelm + * Copyright © 2012-2013 Hans de Goede + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" +#include "linux_usbfs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* sysfs vs usbfs: + * opening a usbfs node causes the device to be resumed, so we attempt to + * avoid this during enumeration. + * + * sysfs allows us to read the kernel's in-memory copies of device descriptors + * and so forth, avoiding the need to open the device: + * - The binary "descriptors" file contains all config descriptors since + * 2.6.26, commit 217a9081d8e69026186067711131b77f0ce219ed + * - The binary "descriptors" file was added in 2.6.23, commit + * 69d42a78f935d19384d1f6e4f94b65bb162b36df, but it only contains the + * active config descriptors + * - The "busnum" file was added in 2.6.22, commit + * 83f7d958eab2fbc6b159ee92bf1493924e1d0f72 + * - The "devnum" file has been present since pre-2.6.18 + * - the "bConfigurationValue" file has been present since pre-2.6.18 + * + * If we have bConfigurationValue, busnum, and devnum, then we can determine + * the active configuration without having to open the usbfs node in RDWR mode. + * The busnum file is important as that is the only way we can relate sysfs + * devices to usbfs nodes. + * + * If we also have all descriptors, we can obtain the device descriptor and + * configuration without touching usbfs at all. + */ + +/* endianness for multi-byte fields: + * + * Descriptors exposed by usbfs have the multi-byte fields in the device + * descriptor as host endian. Multi-byte fields in the other descriptors are + * bus-endian. The kernel documentation says otherwise, but it is wrong. + * + * In sysfs all descriptors are bus-endian. + */ + +#define USBDEV_PATH "/dev" +#define USB_DEVTMPFS_PATH "/dev/bus/usb" + +/* use usbdev*.* device names in /dev instead of the usbfs bus directories */ +static int usbdev_names = 0; + +/* Linux has changed the maximum length of an individual isochronous packet + * over time. Initially this limit was 1,023 bytes, but Linux 2.6.18 + * (commit 3612242e527eb47ee4756b5350f8bdf791aa5ede) increased this value to + * 8,192 bytes to support higher bandwidth devices. Linux 3.10 + * (commit e2e2f0ea1c935edcf53feb4c4c8fdb4f86d57dd9) further increased this + * value to 49,152 bytes to support super speed devices. Linux 5.2 + * (commit 8a1dbc8d91d3d1602282c7e6b4222c7759c916fa) even further increased + * this value to 98,304 bytes to support super speed plus devices. + */ +static unsigned int max_iso_packet_len = 0; + +/* is sysfs available (mounted) ? */ +static int sysfs_available = -1; + +/* how many times have we initted (and not exited) ? */ +static int init_count = 0; + +/* Serialize scan-devices, event-thread, and poll */ +usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER; + +static int open_sysfs_attr(struct libusb_context *ctx, + const char *sysfs_dir, const char *attr); +static int linux_scan_devices(struct libusb_context *ctx); +static int detach_kernel_driver_and_claim(struct libusb_device_handle *, uint8_t); + +#if !defined(HAVE_LIBUDEV) +static int linux_default_scan_devices(struct libusb_context *ctx); +#endif + +struct kernel_version { + int major; + int minor; + int sublevel; +}; + +struct config_descriptor { + struct usbi_configuration_descriptor *desc; + size_t actual_len; +}; + +struct linux_context_priv { + /* no enumeration or hot-plug detection */ + int no_device_discovery; +}; + +struct linux_device_priv { + char *sysfs_dir; + void *descriptors; + size_t descriptors_len; + struct config_descriptor *config_descriptors; + int active_config; /* cache val for !sysfs_available */ +}; + +struct linux_device_handle_priv { + int fd; + int fd_removed; + int fd_keep; + uint32_t caps; +}; + +enum reap_action { + NORMAL = 0, + /* submission failed after the first URB, so await cancellation/completion + * of all the others */ + SUBMIT_FAILED, + + /* cancelled by user or timeout */ + CANCELLED, + + /* completed multi-URB transfer in non-final URB */ + COMPLETED_EARLY, + + /* one or more urbs encountered a low-level error */ + ERROR, +}; + +struct linux_transfer_priv { + union { + struct usbfs_urb *urbs; + struct usbfs_urb **iso_urbs; + }; + + enum reap_action reap_action; + int num_urbs; + int num_retired; + enum libusb_transfer_status reap_status; + + /* next iso packet in user-supplied transfer to be populated */ + 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, int access_mode, int silent) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + char path[24]; + int fd; + + if (usbdev_names) + snprintf(path, sizeof(path), USBDEV_PATH "/usbdev%u.%u", + dev->bus_number, dev->device_address); + else + snprintf(path, sizeof(path), USB_DEVTMPFS_PATH "/%03u/%03u", + dev->bus_number, dev->device_address); + + fd = open(path, access_mode | O_CLOEXEC); + if (fd != -1) + return fd; /* Success */ + + /* TODO: fix race between netlink and usbfs https://github.com/libusb/libusb/issues/1691 */ + if (errno == ENOENT) { + const long delay_ms = 10L; + const struct timespec delay_ts = { 0L, delay_ms * 1000L * 1000L }; + uint8_t retry = 3; + + while (retry-- > 0) { + if (!silent) + usbi_err(ctx, "File doesn't exist, wait %ld ms and try again", delay_ms); + + /* Wait 10ms for USB device path creation.*/ + nanosleep(&delay_ts, NULL); + + fd = open(path, access_mode | O_CLOEXEC); + if (fd != -1) + return fd; /* Success */ + if (errno != ENOENT) + break; + } + } + + if (!silent) { + usbi_err(ctx, "libusb couldn't open USB device %s, errno=%d", path, errno); + if (errno == EACCES && access_mode == O_RDWR) + usbi_err(ctx, "libusb requires write access to USB device nodes"); + } + + if (errno == EACCES) + return LIBUSB_ERROR_ACCESS; + if (errno == ENOENT) + return LIBUSB_ERROR_NO_DEVICE; + return LIBUSB_ERROR_IO; +} + +/* check dirent for a /dev/usbdev%d.%d name + * optionally return bus/device on success */ +static int is_usbdev_entry(const char *name, uint8_t *bus_p, uint8_t *dev_p) +{ + int busnum, devnum; + + if (sscanf(name, "usbdev%d.%d", &busnum, &devnum) != 2) + return 0; + if (busnum < 0 || busnum > UINT8_MAX || devnum < 0 || devnum > UINT8_MAX) { + usbi_dbg(NULL, "invalid usbdev format '%s'", name); + return 0; + } + + usbi_dbg(NULL, "found: %s", name); + if (bus_p) + *bus_p = (uint8_t)busnum; + if (dev_p) + *dev_p = (uint8_t)devnum; + return 1; +} + +static const char *find_usbfs_path(void) +{ + const char *path; + DIR *dir; + struct dirent *entry; + + path = USB_DEVTMPFS_PATH; + dir = opendir(path); + if (dir) { + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + /* We assume if we find any files that it must be the right place */ + break; + } + + closedir(dir); + + if (entry) + return path; + } + + /* look for /dev/usbdev*.* if the normal place fails */ + path = USBDEV_PATH; + dir = opendir(path); + if (dir) { + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + if (is_usbdev_entry(entry->d_name, NULL, NULL)) { + /* found one; that's enough */ + break; + } + } + + closedir(dir); + + if (entry) { + usbdev_names = 1; + return path; + } + } + + return NULL; +} + +static int get_kernel_version(struct libusb_context *ctx, + struct kernel_version *ver) +{ + struct utsname uts; + int atoms; + + if (uname(&uts) < 0) { + usbi_err(ctx, "uname failed, errno=%d", errno); + return -1; + } + + atoms = sscanf(uts.release, "%d.%d.%d", &ver->major, &ver->minor, &ver->sublevel); + if (atoms < 2) { + usbi_err(ctx, "failed to parse uname release '%s'", uts.release); + return -1; + } + + if (atoms < 3) + ver->sublevel = -1; + + usbi_dbg(ctx, "reported kernel version is %s", uts.release); + + return 0; +} + +static int kernel_version_ge(const struct kernel_version *ver, + int major, int minor, int sublevel) +{ + if (ver->major > major) + return 1; + else if (ver->major < major) + return 0; + + /* kmajor == major */ + if (ver->minor > minor) + return 1; + else if (ver->minor < minor) + return 0; + + /* kminor == minor */ + if (ver->sublevel == -1) + return sublevel == 0; + + return ver->sublevel >= sublevel; +} + +static int op_init(struct libusb_context *ctx) +{ + struct kernel_version kversion; + const char *usbfs_path; + int r; + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); + + if (get_kernel_version(ctx, &kversion) < 0) + return LIBUSB_ERROR_OTHER; + + if (!kernel_version_ge(&kversion, 2, 6, 32)) { + usbi_err(ctx, "kernel version is too old (reported as %d.%d.%d)", + kversion.major, kversion.minor, + kversion.sublevel != -1 ? kversion.sublevel : 0); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + usbfs_path = find_usbfs_path(); + if (!usbfs_path) { + /* On udev based systems without any usb devices /dev/bus/usb will not + * exist. On android and other systems SELinux policies might block us + * from reading /dev. Nonetheless it's the default for most modern linux + systems including those using udev alternatives such as busybox's mdev. */ + usbfs_path = USB_DEVTMPFS_PATH; + usbi_dbg(ctx, "could not find usbfs, defaulting to %s", usbfs_path); + } else { + usbi_dbg(ctx, "found usbfs at %s", usbfs_path); + } + + if (!max_iso_packet_len) { + if (kernel_version_ge(&kversion, 5, 2, 0)) + max_iso_packet_len = 98304; + else if (kernel_version_ge(&kversion, 3, 10, 0)) + max_iso_packet_len = 49152; + else + max_iso_packet_len = 8192; + } + + 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(ctx, "sysfs is available"); + sysfs_available = 1; + } else { + usbi_warn(ctx, "sysfs not mounted"); + sysfs_available = 0; + } + } + + if (cpriv->no_device_discovery) { + return LIBUSB_SUCCESS; + } + + r = LIBUSB_SUCCESS; + if (init_count == 0) { + /* start up hotplug event handler */ + r = linux_start_event_monitor(); + } + if (r == LIBUSB_SUCCESS) { + r = linux_scan_devices(ctx); + if (r == LIBUSB_SUCCESS) + init_count++; + else if (init_count == 0) + linux_stop_event_monitor(); + } else { + usbi_err(ctx, "error starting hotplug event monitor"); + } + + return r; +} + +static void op_exit(struct libusb_context *ctx) +{ + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); + + if (cpriv->no_device_discovery) { + return; + } + + assert(init_count != 0); + if (!--init_count) { + /* tear down event handler */ + linux_stop_event_monitor(); + } +} + +static int op_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap) +{ + UNUSED(ap); + + if (option == LIBUSB_OPTION_NO_DEVICE_DISCOVERY) { + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); + + usbi_dbg(ctx, "no device discovery will be performed"); + cpriv->no_device_discovery = 1; + return LIBUSB_SUCCESS; + } + + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int op_get_device_string(struct libusb_device *dev, + enum libusb_device_string_type string_type, char *buffer, int length) +{ + ssize_t r; + int fd; + struct linux_device_priv *priv = usbi_get_device_priv(dev); + struct libusb_context* ctx = DEVICE_CTX(dev); + const char * attr; + + switch (string_type) { + case LIBUSB_DEVICE_STRING_MANUFACTURER: attr = "manufacturer"; break; + case LIBUSB_DEVICE_STRING_PRODUCT: attr = "product"; break; + case LIBUSB_DEVICE_STRING_SERIAL_NUMBER: attr = "serial"; break; + case LIBUSB_DEVICE_STRING_COUNT: + /* intentional fall-through, avoid -Wswitch-enum */ + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + fd = open_sysfs_attr(ctx, priv->sysfs_dir, attr); + if (fd < 0) + return LIBUSB_ERROR_IO; + + r = read(fd, buffer, length - 1); // leave space for null terminator + if (r < 0) { + r = errno; + close(fd); + if (r == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + usbi_err(ctx, "attribute %s read failed, errno=%zd", attr, r); + return LIBUSB_ERROR_IO; + } + close(fd); + buffer[r] = 0; // add null terminator + while (r && ((buffer[r] == 0) || (buffer[r] == '\n'))) { + buffer[r--] = 0; + } + ++r; + return r; +} + +static int linux_scan_devices(struct libusb_context *ctx) +{ + int ret; + + usbi_mutex_static_lock(&linux_hotplug_lock); + +#if defined(HAVE_LIBUDEV) + ret = linux_udev_scan_devices(ctx); +#else + ret = linux_default_scan_devices(ctx); +#endif + + usbi_mutex_static_unlock(&linux_hotplug_lock); + + return ret; +} + +static void op_hotplug_poll(void) +{ + linux_hotplug_poll(); +} + +static int open_sysfs_attr(struct libusb_context *ctx, + const char *sysfs_dir, const char *attr) +{ + char filename[256]; + int fd; + + snprintf(filename, sizeof(filename), SYSFS_DEVICE_PATH "/%s/%s", sysfs_dir, attr); + fd = open(filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + if (errno == ENOENT) { + /* File doesn't exist. Assume the device has been + disconnected (see trac ticket #70). */ + return LIBUSB_ERROR_NO_DEVICE; + } + usbi_err(ctx, "open %s failed, errno=%d", filename, errno); + return LIBUSB_ERROR_IO; + } + + return fd; +} + +/* Note only suitable for attributes which always read >= 0, < 0 is error */ +static int read_sysfs_attr(struct libusb_context *ctx, + const char *sysfs_dir, const char *attr, int max_value, int *value_p) +{ + char buf[20], *endptr; + long value; + ssize_t r; + int fd; + + fd = open_sysfs_attr(ctx, sysfs_dir, attr); + if (fd < 0) + return fd; + + r = read(fd, buf, sizeof(buf) - 1); + if (r < 0) { + r = errno; + close(fd); + if (r == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + usbi_err(ctx, "attribute %s read failed, errno=%zd", attr, r); + return LIBUSB_ERROR_IO; + } + close(fd); + + if (r == 0) { + /* Certain attributes (e.g. bConfigurationValue) are not + * populated if the device is not configured. */ + *value_p = -1; + return 0; + } + + /* 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_warn(ctx, "attribute %s doesn't end with newline?", attr); + } else { + /* Remove the terminating newline character */ + r--; + } + buf[r] = '\0'; + + errno = 0; + value = strtol(buf, &endptr, 10); + if (buf == endptr || value < 0 || value > (long)max_value || errno) { + usbi_err(ctx, "attribute %s contains an invalid value: '%s'", attr, buf); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (*endptr != '\0') { + /* Consider the value to be valid if the remainder is a '.' + * character followed by numbers. This occurs, for example, + * when reading the "speed" attribute for a low-speed device + * (e.g. "1.5") */ + if (*endptr == '.' && isdigit(*(endptr + 1))) { + endptr++; + while (isdigit(*endptr)) + endptr++; + } + if (*endptr != '\0') { + usbi_err(ctx, "attribute %s contains an invalid value: '%s'", attr, buf); + return LIBUSB_ERROR_INVALID_PARAM; + } + } + + *value_p = (int)value; + return 0; +} + +static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) +{ + uint8_t busnum, devaddr; + int ret; + + ret = linux_get_device_address(ctx, 0, &busnum, &devaddr, NULL, devname, -1); + if (ret != LIBUSB_SUCCESS) + return ret; + + return linux_enumerate_device(ctx, busnum, devaddr, devname); +} + +/* read the bConfigurationValue for a device */ +static int sysfs_get_active_config(struct libusb_device *dev, int *config) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + + 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, + uint8_t *busnum, uint8_t *devaddr, const char *dev_node, + const char *sys_name, int fd) +{ + int sysfs_val; + int r; + + 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) { + if (!dev_node && fd >= 0) { + char *fd_path = alloca(PATH_MAX); + char proc_path[32]; + + /* try to retrieve the device node from fd */ + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); + r = readlink(proc_path, fd_path, PATH_MAX - 1); + if (r > 0) { + fd_path[r] = '\0'; + dev_node = fd_path; + } + } + + if (!dev_node) + return LIBUSB_ERROR_OTHER; + + /* will this work with all supported kernel versions? */ + if (!strncmp(dev_node, "/dev/bus/usb", 12)) + sscanf(dev_node, "/dev/bus/usb/%hhu/%hhu", busnum, devaddr); + else + return LIBUSB_ERROR_OTHER; + + return LIBUSB_SUCCESS; + } + + usbi_dbg(ctx, "scan %s", sys_name); + + r = read_sysfs_attr(ctx, sys_name, "busnum", UINT8_MAX, &sysfs_val); + if (r < 0) + return r; + *busnum = (uint8_t)sysfs_val; + + r = read_sysfs_attr(ctx, sys_name, "devnum", UINT8_MAX, &sysfs_val); + if (r < 0) + return r; + *devaddr = (uint8_t)sysfs_val; + + usbi_dbg(ctx, "bus=%u dev=%u", *busnum, *devaddr); + + return LIBUSB_SUCCESS; +} + +/* Return offset of the next config descriptor */ +static int seek_to_next_config(struct libusb_context *ctx, + uint8_t *buffer, size_t len) +{ + struct usbi_descriptor_header *header; + 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) { + usbi_err(ctx, "remaining descriptor length too small %zu/2", len); + return LIBUSB_ERROR_IO; + } + + header = (struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType == LIBUSB_DT_CONFIG) + return offset; + + if (header->bLength < 2) { + usbi_err(ctx, "invalid descriptor bLength %hhu", header->bLength); + return LIBUSB_ERROR_IO; + } + + if (len < header->bLength) { + usbi_err(ctx, "bLength overflow by %zu bytes", + (size_t)header->bLength - len); + return LIBUSB_ERROR_IO; + } + + offset += header->bLength; + buffer += header->bLength; + len -= header->bLength; + } + + usbi_err(ctx, "config descriptor not found"); + return LIBUSB_ERROR_IO; +} + +static int parse_config_descriptors(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct linux_device_priv *priv = usbi_get_device_priv(dev); + struct usbi_device_descriptor *device_desc; + uint8_t idx, num_configs; + uint8_t *buffer; + size_t remaining; + + device_desc = priv->descriptors; + num_configs = device_desc->bNumConfigurations; + + if (num_configs == 0) + return 0; /* no configurations? */ + + priv->config_descriptors = malloc(num_configs * sizeof(priv->config_descriptors[0])); + if (!priv->config_descriptors) + return LIBUSB_ERROR_NO_MEM; + + buffer = (uint8_t *)priv->descriptors + LIBUSB_DT_DEVICE_SIZE; + remaining = priv->descriptors_len - LIBUSB_DT_DEVICE_SIZE; + + for (idx = 0; idx < num_configs; idx++) { + struct usbi_configuration_descriptor *config_desc; + uint16_t config_len; + + if (remaining < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short descriptor read %zu/%d", + remaining, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + config_desc = (struct usbi_configuration_descriptor *)buffer; + if (config_desc->bDescriptorType != LIBUSB_DT_CONFIG) { + usbi_err(ctx, "descriptor is not a config desc (type 0x%02x)", + config_desc->bDescriptorType); + return LIBUSB_ERROR_IO; + } else if (config_desc->bLength < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid descriptor bLength %u", + config_desc->bLength); + return LIBUSB_ERROR_IO; + } + + config_len = libusb_le16_to_cpu(config_desc->wTotalLength); + if (config_len < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid wTotalLength %u", config_len); + return LIBUSB_ERROR_IO; + } + + 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. + */ + uint16_t sysfs_config_len; + int offset; + + if (num_configs > 1 && idx < num_configs - 1) { + offset = seek_to_next_config(ctx, buffer, remaining); + if (offset < 0) + return offset; + sysfs_config_len = (uint16_t)offset; + } else { + sysfs_config_len = (uint16_t)remaining; + } + + if (config_len != sysfs_config_len) { + usbi_warn(ctx, "config length mismatch wTotalLength %u real %u", + config_len, sysfs_config_len); + config_len = sysfs_config_len; + } + } else { + /* + * In usbfs the config descriptors are wTotalLength bytes apart, + * with any short reads from the device appearing as holes in the file. + */ + if (config_len > remaining) { + usbi_warn(ctx, "short descriptor read %zu/%u", remaining, config_len); + config_len = (uint16_t)remaining; + } + } + + 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; + + buffer += config_len; + remaining -= config_len; + } + + return LIBUSB_SUCCESS; +} + +static int op_get_config_descriptor_by_value(struct libusb_device *dev, + uint8_t value, void **buffer) +{ + 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 == value) { + *buffer = config->desc; + return (int)config->actual_len; + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +static int op_get_active_config_descriptor(struct libusb_device *dev, + void *buffer, size_t len) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + void *config_desc; + int active_config; + int r; + + if (priv->sysfs_dir) { + r = sysfs_get_active_config(dev, &active_config); + if (r < 0) + return r; + } else { + /* Use cached bConfigurationValue */ + active_config = priv->active_config; + } + + if (active_config == -1) { + usbi_err(DEVICE_CTX(dev), "device unconfigured"); + return LIBUSB_ERROR_NOT_FOUND; + } + + r = op_get_config_descriptor_by_value(dev, (uint8_t)active_config, &config_desc); + if (r < 0) + return r; + + len = MIN(len, (size_t)r); + memcpy(buffer, config_desc, len); + return len; +} + +static int op_get_config_descriptor(struct libusb_device *dev, + uint8_t config_index, void *buffer, size_t len) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + struct config_descriptor *config; + + if (config_index >= dev->device_descriptor.bNumConfigurations) + return LIBUSB_ERROR_NOT_FOUND; + + config = &priv->config_descriptors[config_index]; + len = MIN(len, config->actual_len); + memcpy(buffer, config->desc, len); + return len; +} + +/* send a control message to retrieve active configuration */ +static int usbfs_get_active_config(struct libusb_device *dev, int fd) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + uint8_t active_config = 0; + int r; + + struct usbfs_ctrltransfer ctrl = { + .bmRequestType = LIBUSB_ENDPOINT_IN, + .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION, + .wValue = 0, + .wIndex = 0, + .wLength = 1, + .timeout = 1000, + .data = &active_config + }; + + r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl); + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + /* 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) { + 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; + } + + return LIBUSB_SUCCESS; +} + +static enum libusb_speed usbfs_get_speed(struct libusb_context *ctx, int fd) +{ + int r; + + r = ioctl(fd, IOCTL_USBFS_GET_SPEED, NULL); + switch (r) { + case USBFS_SPEED_UNKNOWN: return LIBUSB_SPEED_UNKNOWN; + case USBFS_SPEED_LOW: return LIBUSB_SPEED_LOW; + case USBFS_SPEED_FULL: return LIBUSB_SPEED_FULL; + case USBFS_SPEED_HIGH: return LIBUSB_SPEED_HIGH; + case USBFS_SPEED_WIRELESS: return LIBUSB_SPEED_HIGH; + case USBFS_SPEED_SUPER: return LIBUSB_SPEED_SUPER; + case USBFS_SPEED_SUPER_PLUS: return LIBUSB_SPEED_SUPER_PLUS; + default: + usbi_warn(ctx, "Error getting device speed: %d", r); + } + + return LIBUSB_SPEED_UNKNOWN; +} + +static int initialize_device(struct libusb_device *dev, uint8_t busnum, + uint8_t devaddr, const char *sysfs_dir, int wrapped_fd) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + struct libusb_context *ctx = DEVICE_CTX(dev); + size_t alloc_len; + int fd, speed, r; + ssize_t nb; + + dev->bus_number = busnum; + dev->device_address = devaddr; + + if (sysfs_dir) { + priv->sysfs_dir = strdup(sysfs_dir); + if (!priv->sysfs_dir) + return LIBUSB_ERROR_NO_MEM; + + /* Note speed can contain 1.5, in this case read_sysfs_attr() + will stop parsing at the '.' and return 1 */ + if (read_sysfs_attr(ctx, sysfs_dir, "speed", INT_MAX, &speed) == 0) { + switch (speed) { + case 1: dev->speed = LIBUSB_SPEED_LOW; break; + case 12: dev->speed = LIBUSB_SPEED_FULL; break; + case 480: dev->speed = LIBUSB_SPEED_HIGH; break; + case 5000: dev->speed = LIBUSB_SPEED_SUPER; break; + case 10000: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break; + case 20000: dev->speed = LIBUSB_SPEED_SUPER_PLUS_X2; break; + default: + usbi_warn(ctx, "unknown device speed: %d Mbps", speed); + } + } + } else if (wrapped_fd >= 0) { + dev->speed = usbfs_get_speed(ctx, wrapped_fd); + } + + /* cache descriptors in memory */ + if (sysfs_dir) { + fd = open_sysfs_attr(ctx, sysfs_dir, "descriptors"); + } else if (wrapped_fd < 0) { + fd = get_usbfs_fd(dev, O_RDONLY, 0); + } else { + fd = wrapped_fd; + r = lseek(fd, 0, SEEK_SET); + if (r < 0) { + usbi_err(ctx, "lseek failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + } + if (fd < 0) + return fd; + + alloc_len = 0; + do { + const size_t desc_read_length = 256; + uint8_t *read_ptr; + + alloc_len += desc_read_length; + priv->descriptors = usbi_reallocf(priv->descriptors, alloc_len); + if (!priv->descriptors) { + if (fd != wrapped_fd) + close(fd); + return LIBUSB_ERROR_NO_MEM; + } + read_ptr = (uint8_t *)priv->descriptors + priv->descriptors_len; + /* usbfs has holes in the file */ + if (!sysfs_dir) + memset(read_ptr, 0, desc_read_length); + nb = read(fd, read_ptr, desc_read_length); + if (nb < 0) { + usbi_err(ctx, "read descriptor failed, errno=%d", errno); + if (fd != wrapped_fd) + close(fd); + return LIBUSB_ERROR_IO; + } + priv->descriptors_len += (size_t)nb; + } while (priv->descriptors_len == alloc_len); + + if (fd != wrapped_fd) + close(fd); + + if (priv->descriptors_len < LIBUSB_DT_DEVICE_SIZE) { + usbi_err(ctx, "short descriptor read (%zu)", priv->descriptors_len); + return LIBUSB_ERROR_IO; + } + + r = parse_config_descriptors(dev); + if (r < 0) + return r; + + memcpy(&dev->device_descriptor, priv->descriptors, LIBUSB_DT_DEVICE_SIZE); + + if (sysfs_dir) { + /* sysfs descriptors are in bus-endian format */ + usbi_localize_device_descriptor(&dev->device_descriptor); + return LIBUSB_SUCCESS; + } + + /* cache active config */ + if (wrapped_fd < 0) + fd = get_usbfs_fd(dev, O_RDWR, 1); + else + fd = wrapped_fd; + if (fd < 0) { + /* cannot send a control message to determine the active + * config. just assume the first one is active. */ + usbi_warn(ctx, "Missing rw usbfs access; cannot determine " + "active configuration descriptor"); + if (priv->config_descriptors) + priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue; + else + priv->active_config = -1; /* No config dt */ + + return LIBUSB_SUCCESS; + } + + r = usbfs_get_active_config(dev, fd); + if (fd != wrapped_fd) + close(fd); + + return r; +} + +static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device *it; + char *parent_sysfs_dir, *tmp, *end; + int ret, add_parent = 1; + + /* XXX -- can we figure out the topology when using usbfs? */ + if (!sysfs_dir || !strncmp(sysfs_dir, "usb", 3)) { + /* either using usbfs or finding the parent of a root hub */ + return LIBUSB_SUCCESS; + } + + parent_sysfs_dir = strdup(sysfs_dir); + if (!parent_sysfs_dir) + return LIBUSB_ERROR_NO_MEM; + + if ((tmp = strrchr(parent_sysfs_dir, '.')) || + (tmp = strrchr(parent_sysfs_dir, '-'))) { + const char *start = tmp + 1; + long port_number = strtol(start, &end, 10); + if (port_number < 0 || port_number > INT_MAX || start == end || '\0' != *end) { + usbi_warn(ctx, "Can not parse sysfs_dir: %s, unexpected parent info", + parent_sysfs_dir); + free(parent_sysfs_dir); + return LIBUSB_ERROR_OTHER; + } else { + dev->port_number = (int)port_number; + } + *tmp = '\0'; + } else { + usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info", + parent_sysfs_dir); + free(parent_sysfs_dir); + return LIBUSB_SUCCESS; + } + + /* is the parent a root hub? */ + if (!strchr(parent_sysfs_dir, '-')) { + tmp = parent_sysfs_dir; + ret = asprintf(&parent_sysfs_dir, "usb%s", tmp); + free(tmp); + if (ret < 0) + return LIBUSB_ERROR_NO_MEM; + } + +retry: + /* find the parent in the context */ + usbi_mutex_lock(&ctx->usb_devs_lock); + for_each_device(ctx, it) { + struct linux_device_priv *priv = usbi_get_device_priv(it); + + if (priv->sysfs_dir) { + if (!strcmp(priv->sysfs_dir, parent_sysfs_dir)) { + dev->parent_dev = libusb_ref_device(it); + break; + } + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + if (!dev->parent_dev && add_parent) { + 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(ctx, "dev %p (%s) has parent %p (%s) port %u", + (void *) dev, sysfs_dir, (void *) dev->parent_dev, + parent_sysfs_dir, dev->port_number); + + free(parent_sysfs_dir); + + return LIBUSB_SUCCESS; +} + +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) +{ + unsigned long session_id; + struct libusb_device *dev; + int r; + + /* FIXME: session ID is not guaranteed unique as addresses can wrap and + * will be reused. instead we should add a simple sysfs attribute with + * a session ID. */ + session_id = busnum << 8 | devaddr; + 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(ctx, "session_id %lu already exists", session_id); + libusb_unref_device(dev); + return LIBUSB_SUCCESS; + } + + usbi_dbg(ctx, "allocating new device for %u/%u (session %lu)", + busnum, devaddr, session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) + return LIBUSB_ERROR_NO_MEM; + + r = initialize_device(dev, busnum, devaddr, sysfs_dir, -1); + if (r < 0) + goto out; + r = usbi_sanitize_device(dev); + if (r < 0) + goto out; + + r = linux_get_parent_info(dev, sysfs_dir); + if (r < 0) + goto out; +out: + if (r < 0) + libusb_unref_device(dev); + else + usbi_connect_device(dev); + + return r; +} + +void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name) +{ + struct libusb_context *ctx; + + usbi_mutex_static_lock(&active_contexts_lock); + for_each_context(ctx) { + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +void linux_device_disconnected(uint8_t busnum, uint8_t devaddr) +{ + struct libusb_context *ctx; + struct libusb_device *dev; + unsigned long session_id = busnum << 8 | devaddr; + + usbi_mutex_static_lock(&active_contexts_lock); + for_each_context(ctx) { + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } else { + usbi_dbg(ctx, "device not found for session %lx", session_id); + } + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +#if !defined(HAVE_LIBUDEV) +static int parse_u8(const char *str, uint8_t *val_p) +{ + char *endptr; + long num; + + errno = 0; + num = strtol(str, &endptr, 10); + if (num < 0 || num > UINT8_MAX || errno) + return 0; + if (endptr == str || *endptr != '\0') + return 0; + + *val_p = (uint8_t)num; + return 1; +} + +/* open a bus directory and adds all discovered devices to the context */ +static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum) +{ + DIR *dir; + char dirpath[20]; + struct dirent *entry; + int r = LIBUSB_ERROR_IO; + + snprintf(dirpath, sizeof(dirpath), USB_DEVTMPFS_PATH "/%03u", busnum); + usbi_dbg(ctx, "%s", dirpath); + dir = opendir(dirpath); + if (!dir) { + usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno); + /* FIXME: should handle valid race conditions like hub unplugged + * during directory iteration - this is not an error */ + return r; + } + + while ((entry = readdir(dir))) { + uint8_t devaddr; + + if (entry->d_name[0] == '.') + continue; + + if (!parse_u8(entry->d_name, &devaddr)) { + usbi_dbg(ctx, "unknown dir entry %s", entry->d_name); + continue; + } + + if (linux_enumerate_device(ctx, busnum, devaddr, NULL)) { + usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name); + continue; + } + + r = 0; + } + + closedir(dir); + return r; +} + +static int usbfs_get_device_list(struct libusb_context *ctx) +{ + struct dirent *entry; + DIR *buses; + uint8_t busnum, devaddr; + int r = 0; + + if (usbdev_names) + buses = opendir(USBDEV_PATH); + else + buses = opendir(USB_DEVTMPFS_PATH); + + if (!buses) { + if (!usbdev_names && errno == ENOENT) { + /* The path does not exist if there are no devices plugged in */ + return LIBUSB_SUCCESS; + } + usbi_err(ctx, "opendir buses failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(buses))) { + if (entry->d_name[0] == '.') + continue; + + if (usbdev_names) { + if (!is_usbdev_entry(entry->d_name, &busnum, &devaddr)) + continue; + + r = linux_enumerate_device(ctx, busnum, devaddr, NULL); + if (r < 0) { + usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name); + continue; + } + } else { + if (!parse_u8(entry->d_name, &busnum)) { + usbi_dbg(ctx, "unknown dir entry %s", entry->d_name); + continue; + } + + r = usbfs_scan_busdir(ctx, busnum); + if (r < 0) + break; + } + } + + closedir(buses); + return r; + +} + +static int sysfs_get_device_list(struct libusb_context *ctx) +{ + DIR *devices = opendir(SYSFS_DEVICE_PATH); + struct dirent *entry; + int num_devices = 0; + int num_enumerated = 0; + + if (!devices) { + usbi_err(ctx, "opendir devices failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(devices))) { + if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3)) + || strchr(entry->d_name, ':')) + continue; + + num_devices++; + + if (sysfs_scan_device(ctx, entry->d_name)) { + usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name); + continue; + } + + num_enumerated++; + } + + closedir(devices); + + /* successful if at least one device was enumerated or no devices were found */ + if (num_enumerated || !num_devices) + return LIBUSB_SUCCESS; + else + return LIBUSB_ERROR_IO; +} + +static int linux_default_scan_devices(struct libusb_context *ctx) +{ + /* we can retrieve device list and descriptors from sysfs or usbfs. + * sysfs is preferable, because if we use usbfs we end up resuming + * any autosuspended USB devices. however, sysfs is not available + * everywhere, so we need a usbfs fallback too. + */ + if (sysfs_available && sysfs_get_device_list(ctx) == LIBUSB_SUCCESS) + return LIBUSB_SUCCESS; + + return usbfs_get_device_list(ctx); +} +#endif + +static int initialize_handle(struct libusb_device_handle *handle, int fd) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int r; + + hpriv->fd = fd; + + r = ioctl(fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps); + if (r < 0) { + if (errno == ENOTTY) + 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; + } + + return usbi_add_event_source(HANDLE_CTX(handle), hpriv->fd, POLLOUT); +} + +static int op_wrap_sys_device(struct libusb_context *ctx, + struct libusb_device_handle *handle, intptr_t sys_dev) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = (int)sys_dev; + uint8_t busnum, devaddr; + struct usbfs_connectinfo ci; + struct libusb_device *dev; + int r; + + r = linux_get_device_address(ctx, 1, &busnum, &devaddr, NULL, NULL, fd); + if (r < 0) { + r = ioctl(fd, IOCTL_USBFS_CONNECTINFO, &ci); + if (r < 0) { + usbi_err(ctx, "connectinfo failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + /* There is no ioctl to get the bus number. We choose 0 here + * as linux starts numbering buses from 1. */ + busnum = 0; + devaddr = ci.devnum; + } + + /* Session id is unused as we do not add the device to the list of + * connected devices. */ + usbi_dbg(ctx, "allocating new device for fd %d", fd); + dev = usbi_alloc_device(ctx, 0); + if (!dev) + return LIBUSB_ERROR_NO_MEM; + + r = initialize_device(dev, busnum, devaddr, NULL, fd); + if (r < 0) + goto out; + r = usbi_sanitize_device(dev); + if (r < 0) + goto out; + /* Consider the device as connected, but do not add it to the managed + * device list. */ + usbi_atomic_store(&dev->attached, 1); + handle->dev = dev; + + r = initialize_handle(handle, fd); + hpriv->fd_keep = 1; + +out: + if (r < 0) + libusb_unref_device(dev); + return r; +} + +static int op_open(struct libusb_device_handle *handle) +{ + int fd, r; + + fd = get_usbfs_fd(handle->dev, O_RDWR, 0); + if (fd < 0) { + if (fd == LIBUSB_ERROR_NO_DEVICE) { + /* device will still be marked as attached if hotplug monitor thread + * hasn't processed remove event yet */ + usbi_mutex_static_lock(&linux_hotplug_lock); + if (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); + } + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + return fd; + } + + r = initialize_handle(handle, fd); + if (r < 0) + close(fd); + + return r; +} + +static void op_close(struct libusb_device_handle *dev_handle) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(dev_handle); + + /* fd may have already been removed by POLLERR condition in op_handle_events() */ + if (!hpriv->fd_removed) + usbi_remove_event_source(HANDLE_CTX(dev_handle), hpriv->fd); + if (!hpriv->fd_keep) + close(hpriv->fd); +} + +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, &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) + active_config = priv->active_config; + } + if (r < 0) + return r; + + if (active_config == -1) { + usbi_warn(HANDLE_CTX(handle), "device unconfigured"); + active_config = 0; + } + + *config = (uint8_t)active_config; + + return 0; +} + +static int op_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct linux_device_priv *priv = usbi_get_device_priv(handle->dev); + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + int r = ioctl(fd, IOCTL_USBFS_SETCONFIGURATION, &config); + + if (r < 0) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "set configuration failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + /* if necessary, update our cached active config descriptor */ + if (!priv->sysfs_dir) { + if (config == 0 && !dev_has_config0(handle->dev)) + config = -1; + + priv->active_config = config; + } + + return LIBUSB_SUCCESS; +} + +static int claim_interface(struct libusb_device_handle *handle, unsigned int iface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + int r = ioctl(fd, IOCTL_USBFS_CLAIMINTERFACE, &iface); + + if (r < 0) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "claim interface failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int release_interface(struct libusb_device_handle *handle, unsigned int iface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + int r = ioctl(fd, IOCTL_USBFS_RELEASEINTERFACE, &iface); + + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "release interface failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int op_set_interface(struct libusb_device_handle *handle, uint8_t interface, + uint8_t altsetting) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + struct usbfs_setinterface setintf; + int r; + + setintf.interface = interface; + setintf.altsetting = altsetting; + r = ioctl(fd, IOCTL_USBFS_SETINTERFACE, &setintf); + if (r < 0) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "set interface failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_clear_halt(struct libusb_device_handle *handle, + unsigned char endpoint) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + unsigned int _endpoint = endpoint; + int r = ioctl(fd, IOCTL_USBFS_CLEAR_HALT, &_endpoint); + + if (r < 0) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "clear halt failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_reset_device(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + int r, ret = 0; + uint8_t i; + + /* Doing a device reset will cause the usbfs driver to get unbound + * from any interfaces it is bound to. By voluntarily unbinding + * the usbfs driver ourself, we stop the kernel from rebinding + * the interface after reset (which would end up with the interface + * getting bound to the in kernel driver if any). */ + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (handle->claimed_interfaces & (1UL << i)) + release_interface(handle, i); + } + + usbi_mutex_lock(&handle->lock); + r = ioctl(fd, IOCTL_USBFS_RESET, NULL); + if (r < 0) { + if (errno == ENODEV) { + ret = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + usbi_err(HANDLE_CTX(handle), "reset failed, errno=%d", errno); + ret = LIBUSB_ERROR_OTHER; + goto out; + } + + /* And re-claim any interfaces which were claimed before the reset */ + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (!(handle->claimed_interfaces & (1UL << i))) + continue; + /* + * A driver may have completed modprobing during + * IOCTL_USBFS_RESET, and bound itself as soon as + * IOCTL_USBFS_RESET released the device lock + */ + r = detach_kernel_driver_and_claim(handle, i); + if (r) { + usbi_warn(HANDLE_CTX(handle), "failed to re-claim interface %u after reset: %s", + i, libusb_error_name(r)); + handle->claimed_interfaces &= ~(1UL << i); + ret = LIBUSB_ERROR_NOT_FOUND; + } + } +out: + usbi_mutex_unlock(&handle->lock); + return ret; +} + +static int do_streams_ioctl(struct libusb_device_handle *handle, + unsigned long req, uint32_t num_streams, unsigned char *endpoints, + int num_endpoints) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int r, fd = hpriv->fd; + struct usbfs_streams *streams; + + if (num_endpoints > 30) /* Max 15 in + 15 out eps */ + return LIBUSB_ERROR_INVALID_PARAM; + + streams = malloc(sizeof(*streams) + num_endpoints); + if (!streams) + return LIBUSB_ERROR_NO_MEM; + + streams->num_streams = num_streams; + streams->num_eps = num_endpoints; + memcpy(streams->eps, endpoints, num_endpoints); + + r = ioctl(fd, req, streams); + + free(streams); + + if (r < 0) { + if (errno == ENOTTY) + return LIBUSB_ERROR_NOT_SUPPORTED; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "streams-ioctl failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + return r; +} + +static int op_alloc_streams(struct libusb_device_handle *handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints) +{ + return do_streams_ioctl(handle, IOCTL_USBFS_ALLOC_STREAMS, + num_streams, endpoints, num_endpoints); +} + +static int op_free_streams(struct libusb_device_handle *handle, + unsigned char *endpoints, int num_endpoints) +{ + return do_streams_ioctl(handle, IOCTL_USBFS_FREE_STREAMS, 0, + endpoints, num_endpoints); +} + +static void *op_dev_mem_alloc(struct libusb_device_handle *handle, size_t len) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + void *buffer; + + buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0); + if (buffer == MAP_FAILED) { + usbi_err(HANDLE_CTX(handle), "alloc dev mem failed, errno=%d", errno); + return NULL; + } + return buffer; +} + +static int op_dev_mem_free(struct libusb_device_handle *handle, void *buffer, + size_t len) +{ + if (munmap(buffer, len) != 0) { + usbi_err(HANDLE_CTX(handle), "free dev mem failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } else { + return LIBUSB_SUCCESS; + } +} + +static int op_kernel_driver_active(struct libusb_device_handle *handle, + uint8_t interface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + struct usbfs_getdriver getdrv; + int r; + + getdrv.interface = interface; + r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); + if (r < 0) { + if (errno == ENODATA) + return 0; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "get driver failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return strcmp(getdrv.driver, "usbfs") != 0; +} + +static int op_detach_kernel_driver(struct libusb_device_handle *handle, + uint8_t interface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + struct usbfs_ioctl command; + struct usbfs_getdriver getdrv; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_DISCONNECT; + command.data = NULL; + + getdrv.interface = interface; + r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); + if (r == 0 && !strcmp(getdrv.driver, "usbfs")) + return LIBUSB_ERROR_NOT_FOUND; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r < 0) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "detach failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_attach_kernel_driver(struct libusb_device_handle *handle, + uint8_t interface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int fd = hpriv->fd; + struct usbfs_ioctl command; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_CONNECT; + command.data = NULL; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r < 0) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + + usbi_err(HANDLE_CTX(handle), "attach failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } else if (r == 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + return 0; +} + +static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle, + uint8_t interface) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + struct usbfs_disconnect_claim dc; + int r, fd = hpriv->fd; + + dc.interface = interface; + strcpy(dc.driver, "usbfs"); + dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER; + r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc); + if (r == 0) + return 0; + switch (errno) { + case ENOTTY: + break; + case EBUSY: + return LIBUSB_ERROR_BUSY; + case EINVAL: + return LIBUSB_ERROR_INVALID_PARAM; + case ENODEV: + return LIBUSB_ERROR_NO_DEVICE; + default: + usbi_err(HANDLE_CTX(handle), "disconnect-and-claim failed, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + /* Fallback code for kernels which don't support the + disconnect-and-claim ioctl */ + r = op_detach_kernel_driver(handle, interface); + if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND) + return r; + + return claim_interface(handle, interface); +} + +static int op_claim_interface(struct libusb_device_handle *handle, uint8_t interface) +{ + if (handle->auto_detach_kernel_driver) + return detach_kernel_driver_and_claim(handle, interface); + else + return claim_interface(handle, interface); +} + +static int op_release_interface(struct libusb_device_handle *handle, uint8_t interface) +{ + int r; + + r = release_interface(handle, interface); + if (r) + return r; + + if (handle->auto_detach_kernel_driver) + op_attach_kernel_driver(handle, interface); + + return 0; +} + +static void op_destroy_device(struct libusb_device *dev) +{ + struct linux_device_priv *priv = usbi_get_device_priv(dev); + + free(priv->config_descriptors); + free(priv->descriptors); + free(priv->sysfs_dir); +} + +/* URBs are discarded in reverse order of submission to avoid races. */ +static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plus_one) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct linux_device_handle_priv *hpriv = + usbi_get_device_handle_priv(transfer->dev_handle); + int i, ret = 0; + struct usbfs_urb *urb; + + for (i = last_plus_one - 1; i >= first; i--) { + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + urb = tpriv->iso_urbs[i]; + else + urb = &tpriv->urbs[i]; + + if (ioctl(hpriv->fd, IOCTL_USBFS_DISCARDURB, urb) == 0) + continue; + + if (errno == EINVAL) { + 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(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); + ret = LIBUSB_ERROR_OTHER; + } + } + return ret; +} + +static void free_iso_urbs(struct linux_transfer_priv *tpriv) +{ + int i; + + for (i = 0; i < tpriv->num_urbs; i++) { + struct usbfs_urb *urb = tpriv->iso_urbs[i]; + + if (!urb) + break; + free(urb); + } + + free(tpriv->iso_urbs); + tpriv->iso_urbs = NULL; +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct linux_device_handle_priv *hpriv = + usbi_get_device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urbs; + int is_out = IS_XFEROUT(transfer); + int bulk_buffer_len, use_bulk_continuation; + int num_urbs; + int last_urb_partial = 0; + int r; + int i; + + /* + * Older versions of usbfs place a 16kb limit on bulk URBs. We work + * around this by splitting large transfers into 16k blocks, and then + * submit all urbs at once. it would be simpler to submit one urb at + * a time, but there is a big performance gain doing it this way. + * + * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), + * using arbitrary large transfers can still be a bad idea though, as + * the kernel needs to allocate physical contiguous memory for this, + * which may fail for large buffers. + * + * The kernel solves this problem by splitting the transfer into + * blocks itself when the host-controller is scatter-gather capable + * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are. + * + * Last, there is the issue of short-transfers when splitting, for + * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION + * is needed, but this is not always available. + */ + if (hpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) { + /* Good! Just submit everything in one go */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else if (hpriv->caps & USBFS_CAP_BULK_CONTINUATION) { + /* Split the transfers and use bulk-continuation to + avoid issues with short-transfers */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 1; + } else if (hpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) { + /* Don't split, assume the kernel can alloc the buffer + (otherwise the submit will fail with -ENOMEM) */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else { + /* Bad, splitting without bulk-continuation, short transfers + which end before the last urb will not work reliable! */ + /* Note we don't warn here as this is "normal" on kernels < + 2.6.32 and not a problem for most applications */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 0; + } + + num_urbs = transfer->length / bulk_buffer_len; + + if (transfer->length == 0) { + num_urbs = 1; + } else if ((transfer->length % bulk_buffer_len) > 0) { + last_urb_partial = 1; + num_urbs++; + } + 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; + tpriv->urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED; + + for (i = 0; i < num_urbs; i++) { + struct usbfs_urb *urb = &urbs[i]; + + urb->usercontext = itransfer; + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + urb->type = USBFS_URB_TYPE_BULK; + urb->stream_id = 0; + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + urb->type = USBFS_URB_TYPE_BULK; + urb->stream_id = itransfer->stream_id; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + urb->type = USBFS_URB_TYPE_INTERRUPT; + break; + } + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer + (i * bulk_buffer_len); + + /* don't set the short not ok flag for the last URB */ + if (use_bulk_continuation && !is_out && (i < num_urbs - 1)) + urb->flags = USBFS_URB_SHORT_NOT_OK; + + if (i == num_urbs - 1 && last_urb_partial) + urb->buffer_length = transfer->length % bulk_buffer_len; + else if (transfer->length == 0) + urb->buffer_length = 0; + else + urb->buffer_length = bulk_buffer_len; + + if (i > 0 && use_bulk_continuation) + urb->flags |= USBFS_URB_BULK_CONTINUATION; + + /* we have already checked that the flag is supported */ + if (is_out && i == num_urbs - 1 && + (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)) + urb->flags |= USBFS_URB_ZERO_PACKET; + + r = ioctl(hpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r == 0) + continue; + + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else if (errno == ENOMEM) { + r = LIBUSB_ERROR_NO_MEM; + } else { + usbi_err(TRANSFER_CTX(transfer), "submiturb failed, errno=%d", errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg(TRANSFER_CTX(transfer), "first URB failed, easy peasy"); + free(urbs); + tpriv->urbs = NULL; + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we may need to discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * - this URB failing may be no error; EREMOTEIO means that + * this transfer simply didn't need all the URBs we submitted + * so, we report that the transfer was submitted successfully and + * in case of error we discard all previous URBs. later when + * the final reap completes we can report error to the user, + * or success if an earlier URB was completed successfully. + */ + tpriv->reap_action = errno == EREMOTEIO ? COMPLETED_EARLY : SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired += num_urbs - i; + + /* If we completed short then don't try to discard. */ + if (tpriv->reap_action == COMPLETED_EARLY) + return 0; + + discard_urbs(itransfer, 0, i); + + usbi_dbg(TRANSFER_CTX(transfer), "reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + + return 0; +} + +static int submit_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct linux_device_handle_priv *hpriv = + usbi_get_device_handle_priv(transfer->dev_handle); + struct usbfs_urb **urbs; + int num_packets = transfer->num_iso_packets; + int num_packets_remaining; + int i, j; + int num_urbs; + unsigned int packet_len; + unsigned int total_len = 0; + unsigned char *urb_buffer = transfer->buffer; + + if (num_packets < 1) + return LIBUSB_ERROR_INVALID_PARAM; + + /* usbfs places arbitrary limits on iso URBs. this limit has changed + * at least three times, but we attempt to detect this limit during + * init and check it here. if the kernel rejects the request due to + * its size, we return an error indicating such to the user. + */ + for (i = 0; i < num_packets; i++) { + packet_len = transfer->iso_packet_desc[i].length; + + if (packet_len > max_iso_packet_len) { + usbi_warn(TRANSFER_CTX(transfer), + "iso packet length of %u bytes exceeds maximum of %u bytes", + packet_len, max_iso_packet_len); + return LIBUSB_ERROR_INVALID_PARAM; + } + + total_len += packet_len; + } + + if (transfer->length < (int)total_len) + return LIBUSB_ERROR_INVALID_PARAM; + + /* 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(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; + + tpriv->iso_urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->iso_packet_offset = 0; + + /* allocate + initialize each URB with the correct number of packets */ + num_packets_remaining = num_packets; + for (i = 0, j = 0; i < num_urbs; i++) { + int num_packets_in_urb = MIN(num_packets_remaining, MAX_ISO_PACKETS_PER_URB); + struct usbfs_urb *urb; + size_t alloc_size; + int k; + + alloc_size = sizeof(*urb) + + (num_packets_in_urb * sizeof(struct usbfs_iso_packet_desc)); + urb = calloc(1, alloc_size); + if (!urb) { + free_iso_urbs(tpriv); + return LIBUSB_ERROR_NO_MEM; + } + urbs[i] = urb; + + /* populate packet lengths */ + for (k = 0; k < num_packets_in_urb; j++, k++) { + packet_len = transfer->iso_packet_desc[j].length; + urb->buffer_length += packet_len; + urb->iso_frame_desc[k].length = packet_len; + } + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_ISO; + /* FIXME: interface for non-ASAP data? */ + urb->flags = USBFS_URB_ISO_ASAP; + urb->endpoint = transfer->endpoint; + urb->number_of_packets = num_packets_in_urb; + urb->buffer = urb_buffer; + + urb_buffer += urb->buffer_length; + num_packets_remaining -= num_packets_in_urb; + } + + /* submit URBs */ + for (i = 0; i < num_urbs; i++) { + int r = ioctl(hpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]); + + if (r == 0) + continue; + + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else if (errno == EINVAL) { + usbi_warn(TRANSFER_CTX(transfer), "submiturb failed, transfer too large"); + r = LIBUSB_ERROR_INVALID_PARAM; + } else if (errno == EMSGSIZE) { + usbi_warn(TRANSFER_CTX(transfer), "submiturb failed, iso packet length too large"); + r = LIBUSB_ERROR_INVALID_PARAM; + } else { + usbi_err(TRANSFER_CTX(transfer), "submiturb failed, errno=%d", errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg(TRANSFER_CTX(transfer), "first URB failed, easy peasy"); + free_iso_urbs(tpriv); + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we must discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * so, in this case we discard all the previous URBs BUT we report + * that the transfer was submitted successfully. then later when + * the final discard completes we can report error to the user. + */ + tpriv->reap_action = SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired = num_urbs - i; + discard_urbs(itransfer, 0, i); + + usbi_dbg(TRANSFER_CTX(transfer), "reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + + return 0; +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *hpriv = + usbi_get_device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urb; + int r; + + if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + urb = calloc(1, sizeof(*urb)); + if (!urb) + return LIBUSB_ERROR_NO_MEM; + tpriv->urbs = urb; + tpriv->num_urbs = 1; + tpriv->reap_action = NORMAL; + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_CONTROL; + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer; + urb->buffer_length = transfer->length; + + r = ioctl(hpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r < 0) { + free(urb); + tpriv->urbs = NULL; + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(TRANSFER_CTX(transfer), "submiturb failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + return 0; +} + +static int op_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), "unknown transfer type %u", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int op_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int r; + + if (!tpriv->urbs) + return LIBUSB_ERROR_NOT_FOUND; + + r = discard_urbs(itransfer, 0, tpriv->num_urbs); + if (r != 0) + return r; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + if (tpriv->reap_action == ERROR) + break; + /* else, fall through */ + default: + tpriv->reap_action = CANCELLED; + } + + return 0; +} + +static void op_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (tpriv->urbs) { + free(tpriv->urbs); + tpriv->urbs = NULL; + } + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (tpriv->iso_urbs) { + free_iso_urbs(tpriv); + tpriv->iso_urbs = NULL; + } + break; + default: + usbi_err(TRANSFER_CTX(transfer), "unknown transfer type %u", transfer->type); + } +} + +static int handle_bulk_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int urb_idx = urb - tpriv->urbs; + + usbi_mutex_lock(&itransfer->lock); + 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(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. + * examples: + * 1. while the kernel is cancelling all the packets that make up an + * URB, a few of them might complete. so we get back a successful + * cancellation *and* some data. + * 2. we receive a short URB which marks the early completion condition, + * so we start cancelling the remaining URBs. however, we're too + * slow and another URB completes (or at least completes partially). + * (this can't happen since we always use BULK_CONTINUATION.) + * + * When this happens, our objectives are not to lose any "surplus" data, + * and also to stick it at the end of the previously-received data + * (closing any holes), so that libusb reports the total amount of + * transferred data and presents it in a contiguous chunk. + */ + if (urb->actual_length > 0) { + unsigned char *target = transfer->buffer + itransfer->transferred; + + usbi_dbg(TRANSFER_CTX(transfer), "received %d bytes of surplus data", urb->actual_length); + if (urb->buffer != target) { + 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); + } + itransfer->transferred += urb->actual_length; + } + + if (tpriv->num_retired == tpriv->num_urbs) { + 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; + goto completed; + } + goto out_unlock; + } + + itransfer->transferred += urb->actual_length; + + /* Many of these errors can occur on *any* urb of a multi-urb + * transfer. When they do, we tear down the rest of the transfer. + */ + switch (urb->status) { + case 0: + break; + case -EREMOTEIO: /* short transfer */ + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg(TRANSFER_CTX(transfer), "device removed"); + tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE; + goto cancel_remaining; + case -EPIPE: + 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(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; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + usbi_dbg(TRANSFER_CTX(transfer), "low-level bus error %d", urb->status); + tpriv->reap_action = ERROR; + goto cancel_remaining; + default: + usbi_warn(ITRANSFER_CTX(itransfer), "unrecognised urb status %d", urb->status); + tpriv->reap_action = ERROR; + goto cancel_remaining; + } + + /* 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(TRANSFER_CTX(transfer), "all URBs in transfer reaped --> complete!"); + goto completed; + } else if (urb->actual_length < urb->buffer_length) { + 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; + } else { + goto out_unlock; + } + +cancel_remaining: + if (tpriv->reap_action == ERROR && tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + + if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */ + goto completed; + + /* cancel remaining urbs and wait for their completion before + * reporting results */ + discard_urbs(itransfer, urb_idx + 1, tpriv->num_urbs); + +out_unlock: + usbi_mutex_unlock(&itransfer->lock); + return 0; + +completed: + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return tpriv->reap_action == CANCELLED ? + usbi_handle_transfer_cancellation(itransfer) : + usbi_handle_transfer_completion(itransfer, tpriv->reap_status); +} + +static int handle_iso_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + int num_urbs = tpriv->num_urbs; + int urb_idx = 0; + int i; + enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; + + usbi_mutex_lock(&itransfer->lock); + for (i = 0; i < num_urbs; i++) { + if (urb == tpriv->iso_urbs[i]) { + urb_idx = i + 1; + break; + } + } + if (urb_idx == 0) { + usbi_err(TRANSFER_CTX(transfer), "could not locate urb!"); + usbi_mutex_unlock(&itransfer->lock); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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 */ + + for (i = 0; i < urb->number_of_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[tpriv->iso_packet_offset++]; + + lib_desc->status = LIBUSB_TRANSFER_COMPLETED; + switch (urb_desc->status) { + case 0: + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg(TRANSFER_CTX(transfer), "packet %d - device removed", i); + lib_desc->status = LIBUSB_TRANSFER_NO_DEVICE; + break; + case -EPIPE: + usbi_dbg(TRANSFER_CTX(transfer), "packet %d - detected endpoint stall", i); + lib_desc->status = LIBUSB_TRANSFER_STALL; + break; + case -EOVERFLOW: + usbi_dbg(TRANSFER_CTX(transfer), "packet %d - overflow error", i); + lib_desc->status = LIBUSB_TRANSFER_OVERFLOW; + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + case -EXDEV: + usbi_dbg(TRANSFER_CTX(transfer), "packet %d - low-level USB error %d", i, urb_desc->status); + lib_desc->status = LIBUSB_TRANSFER_ERROR; + break; + default: + usbi_warn(TRANSFER_CTX(transfer), "packet %d - unrecognised urb status %d", + i, urb_desc->status); + lib_desc->status = LIBUSB_TRANSFER_ERROR; + break; + } + lib_desc->actual_length = urb_desc->actual_length; + } + + tpriv->num_retired++; + + if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */ + usbi_dbg(TRANSFER_CTX(transfer), "CANCEL: urb status %d", urb->status); + + if (tpriv->num_retired == num_urbs) { + usbi_dbg(TRANSFER_CTX(transfer), "CANCEL: last URB handled, reporting"); + free_iso_urbs(tpriv); + if (tpriv->reap_action == CANCELLED) { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } else { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_ERROR); + } + } + goto out; + } + + switch (urb->status) { + case 0: + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ESHUTDOWN: + usbi_dbg(TRANSFER_CTX(transfer), "device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + default: + usbi_warn(TRANSFER_CTX(transfer), "unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + /* if we've reaped all urbs then we're done */ + if (tpriv->num_retired == num_urbs) { + 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); + } + +out: + usbi_mutex_unlock(&itransfer->lock); + return 0; +} + +static int handle_control_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + int status; + + usbi_mutex_lock(&itransfer->lock); + usbi_dbg(ITRANSFER_CTX(itransfer), "handling completion status %d", urb->status); + + itransfer->transferred += urb->actual_length; + + if (tpriv->reap_action == CANCELLED) { + if (urb->status && urb->status != -ENOENT) + usbi_warn(ITRANSFER_CTX(itransfer), "cancel: unrecognised urb status %d", + urb->status); + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } + + switch (urb->status) { + case 0: + status = LIBUSB_TRANSFER_COMPLETED; + break; + case -ENOENT: /* cancelled */ + status = LIBUSB_TRANSFER_CANCELLED; + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg(ITRANSFER_CTX(itransfer), "device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + case -EPIPE: + usbi_dbg(ITRANSFER_CTX(itransfer), "unsupported control request"); + status = LIBUSB_TRANSFER_STALL; + break; + case -EOVERFLOW: + usbi_dbg(ITRANSFER_CTX(itransfer), "overflow, actual_length=%d", urb->actual_length); + status = LIBUSB_TRANSFER_OVERFLOW; + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + usbi_dbg(ITRANSFER_CTX(itransfer), "low-level bus error %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + default: + usbi_warn(ITRANSFER_CTX(itransfer), "unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); +} + +static int reap_for_handle(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int r; + struct usbfs_urb *urb = NULL; + struct usbi_transfer *itransfer; + struct libusb_transfer *transfer; + + r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb); + if (r < 0) { + if (errno == EAGAIN) + return 1; + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "reap failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + itransfer = urb->usercontext; + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + 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: + return handle_iso_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return handle_bulk_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_CONTROL: + return handle_control_completion(itransfer, urb); + default: + usbi_err(HANDLE_CTX(handle), "unrecognised transfer type %u", transfer->type); + return LIBUSB_ERROR_OTHER; + } +} + +static int op_handle_events(struct libusb_context *ctx, + void *event_data, unsigned int count, unsigned int num_ready) +{ + struct pollfd *fds = event_data; + unsigned int n; + int r; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (n = 0; n < count && num_ready > 0; n++) { + struct pollfd *pollfd = &fds[n]; + struct libusb_device_handle *handle; + struct linux_device_handle_priv *hpriv = NULL; + int reap_count; + + if (!pollfd->revents) + continue; + + num_ready--; + for_each_open_device(ctx, handle) { + hpriv = usbi_get_device_handle_priv(handle); + if (hpriv->fd == pollfd->fd) + break; + } + + if (!hpriv || hpriv->fd != pollfd->fd) { + usbi_err(ctx, "cannot find handle for fd %d", + pollfd->fd); + continue; + } + + if (pollfd->revents & POLLERR) { + /* remove the fd from the pollfd set so that it doesn't continuously + * trigger an event, and flag that it has been removed so op_close() + * doesn't try to remove it a second time */ + usbi_remove_event_source(HANDLE_CTX(handle), hpriv->fd); + hpriv->fd_removed = 1; + + /* 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 (usbi_atomic_load(&handle->dev->attached)) + linux_device_disconnected(handle->dev->bus_number, + handle->dev->device_address); + usbi_mutex_static_unlock(&linux_hotplug_lock); + + if (hpriv->caps & USBFS_CAP_REAP_AFTER_DISCONNECT) { + do { + r = reap_for_handle(handle); + } while (r == 0); + } + + usbi_handle_disconnect(ctx, handle); + continue; + } + + reap_count = 0; + do { + r = reap_for_handle(handle); + } while (r == 0 && ++reap_count <= 25); + + if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE) + continue; + else if (r < 0) + goto out; + } + + r = 0; +out: + usbi_mutex_unlock(&ctx->open_devs_lock); + return r; +} + +const struct usbi_os_backend usbi_backend = { + .name = "Linux usbfs", + .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, + .init = op_init, + .exit = op_exit, + .set_option = op_set_option, + .get_device_string = op_get_device_string, + .hotplug_poll = op_hotplug_poll, + .get_active_config_descriptor = op_get_active_config_descriptor, + .get_config_descriptor = op_get_config_descriptor, + .get_config_descriptor_by_value = op_get_config_descriptor_by_value, + + .wrap_sys_device = op_wrap_sys_device, + .open = op_open, + .close = op_close, + .get_configuration = op_get_configuration, + .set_configuration = op_set_configuration, + .claim_interface = op_claim_interface, + .release_interface = op_release_interface, + + .set_interface_altsetting = op_set_interface, + .clear_halt = op_clear_halt, + .reset_device = op_reset_device, + + .alloc_streams = op_alloc_streams, + .free_streams = op_free_streams, + + .dev_mem_alloc = op_dev_mem_alloc, + .dev_mem_free = op_dev_mem_free, + + .kernel_driver_active = op_kernel_driver_active, + .detach_kernel_driver = op_detach_kernel_driver, + .attach_kernel_driver = op_attach_kernel_driver, + + .destroy_device = op_destroy_device, + + .submit_transfer = op_submit_transfer, + .cancel_transfer = op_cancel_transfer, + .clear_transfer_priv = op_clear_transfer_priv, + + .handle_events = op_handle_events, + + .context_priv_size = sizeof(struct linux_context_priv), + .device_priv_size = sizeof(struct linux_device_priv), + .device_handle_priv_size = sizeof(struct linux_device_handle_priv), + .transfer_priv_size = sizeof(struct linux_transfer_priv), +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.h new file mode 100644 index 00000000..5c646742 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/linux_usbfs.h @@ -0,0 +1,221 @@ +/* + * usbfs header structures + * Copyright © 2007 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_USBFS_H +#define LIBUSB_USBFS_H + +#include +#include + +#define SYSFS_MOUNT_PATH "/sys" +#define SYSFS_DEVICE_PATH SYSFS_MOUNT_PATH "/bus/usb/devices" + +struct usbfs_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + __u8 bmRequestType; + __u8 bRequest; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + __u32 timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USBFS_MAXDRIVERNAME 255 + +struct usbfs_getdriver { + unsigned int interface; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 +#define USBFS_URB_ZERO_PACKET 0x40 + +#define USBFS_URB_TYPE_ISO 0 +#define USBFS_URB_TYPE_INTERRUPT 1 +#define USBFS_URB_TYPE_CONTROL 2 +#define USBFS_URB_TYPE_BULK 3 + +struct usbfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +#define MAX_BULK_BUFFER_LENGTH 16384 +#define MAX_CTRL_BUFFER_LENGTH 4096 + +#define MAX_ISO_PACKETS_PER_URB 128 + +struct usbfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + union { + int number_of_packets; /* Only used for isoc urbs */ + unsigned int stream_id; /* Only used with bulk streams */ + }; + int error_count; + unsigned int signr; + void *usercontext; + struct usbfs_iso_packet_desc iso_frame_desc[0]; +}; + +struct usbfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usbfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +#define USBFS_CAP_ZERO_PACKET 0x01 +#define USBFS_CAP_BULK_CONTINUATION 0x02 +#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBFS_CAP_BULK_SCATTER_GATHER 0x08 +#define USBFS_CAP_REAP_AFTER_DISCONNECT 0x10 + +#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 +#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 + +struct usbfs_disconnect_claim { + unsigned int interface; + unsigned int flags; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +struct usbfs_streams { + unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */ + unsigned int num_eps; + unsigned char eps[0]; +}; + +#define USBFS_SPEED_UNKNOWN 0 +#define USBFS_SPEED_LOW 1 +#define USBFS_SPEED_FULL 2 +#define USBFS_SPEED_HIGH 3 +#define USBFS_SPEED_WIRELESS 4 +#define USBFS_SPEED_SUPER 5 +#define USBFS_SPEED_SUPER_PLUS 6 + +#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) +#define IOCTL_USBFS_SETINTERFACE _IOR('U', 4, struct usbfs_setinterface) +#define IOCTL_USBFS_SETCONFIGURATION _IOR('U', 5, unsigned int) +#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) +#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) +#define IOCTL_USBFS_DISCARDURB _IO('U', 11) +#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USBFS_CLAIMINTERFACE _IOR('U', 15, unsigned int) +#define IOCTL_USBFS_RELEASEINTERFACE _IOR('U', 16, unsigned int) +#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) +#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) +#define IOCTL_USBFS_RESET _IO('U', 20) +#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) +#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32) +#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim) +#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams) +#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams) +#define IOCTL_USBFS_DROP_PRIVILEGES _IOW('U', 30, __u32) +#define IOCTL_USBFS_GET_SPEED _IO('U', 31) + +extern usbi_mutex_static_t linux_hotplug_lock; + +#ifdef HAVE_LIBUDEV +int linux_udev_start_event_monitor(void); +int linux_udev_stop_event_monitor(void); +int linux_udev_scan_devices(struct libusb_context *ctx); +void linux_udev_hotplug_poll(void); +#else +int linux_netlink_start_event_monitor(void); +int linux_netlink_stop_event_monitor(void); +void linux_netlink_hotplug_poll(void); +#endif + +static inline int linux_start_event_monitor(void) +{ +#if defined(HAVE_LIBUDEV) + return linux_udev_start_event_monitor(); +/* +* __ANDROID__: preprocessor macro defined automatically by GCC for all Android +* targets (i.e. both Android native applications, and Android OS-level +* services) +* +* ANDROID_OS: compilation flag that should be set for using libusb from programs +* running on Android at OS level (e.g. Android platform services). +* The programs using libusb built with the ANDROID_OS flag must have +* permission to access netlink sockets. +*/ +#elif !defined(__ANDROID__) || defined(ANDROID_OS) + return linux_netlink_start_event_monitor(); +#else + return LIBUSB_SUCCESS; +#endif +} + +static inline void linux_stop_event_monitor(void) +{ +#if defined(HAVE_LIBUDEV) + linux_udev_stop_event_monitor(); +#elif !defined(__ANDROID__) || defined(ANDROID_OS) + linux_netlink_stop_event_monitor(); +#endif +} + +static inline void linux_hotplug_poll(void) +{ +#if defined(HAVE_LIBUDEV) + linux_udev_hotplug_poll(); +#elif !defined(__ANDROID__) || defined(ANDROID_OS) + linux_netlink_hotplug_poll(); +#endif +} + +void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name); +void linux_device_disconnected(uint8_t busnum, uint8_t devaddr); + +int linux_get_device_address(struct libusb_context *ctx, int detached, + uint8_t *busnum, uint8_t *devaddr, const char *dev_node, + const char *sys_name, int fd); +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir); + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/netbsd_usb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/netbsd_usb.c new file mode 100644 index 00000000..f29dc7a1 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/netbsd_usb.c @@ -0,0 +1,619 @@ +/* + * Copyright © 2011 Martin Pieuchot + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "libusbi.h" + +struct device_priv { + char devnode[16]; + int fd; + + usb_config_descriptor_t *cdesc; /* active config descriptor */ +}; + +struct handle_priv { + int endpoints[USB_MAX_ENDPOINTS]; +}; + +/* + * Backend functions + */ +static int netbsd_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int netbsd_open(struct libusb_device_handle *); +static void netbsd_close(struct libusb_device_handle *); + +static int netbsd_get_active_config_descriptor(struct libusb_device *, + void *, size_t); +static int netbsd_get_config_descriptor(struct libusb_device *, uint8_t, + void *, size_t); + +static int netbsd_get_configuration(struct libusb_device_handle *, uint8_t *); +static int netbsd_set_configuration(struct libusb_device_handle *, int); + +static int netbsd_claim_interface(struct libusb_device_handle *, uint8_t); +static int netbsd_release_interface(struct libusb_device_handle *, uint8_t); + +static int netbsd_set_interface_altsetting(struct libusb_device_handle *, + uint8_t, uint8_t); +static int netbsd_clear_halt(struct libusb_device_handle *, unsigned char); +static void netbsd_destroy_device(struct libusb_device *); + +static int netbsd_submit_transfer(struct usbi_transfer *); +static int netbsd_cancel_transfer(struct usbi_transfer *); +static int netbsd_handle_transfer_completion(struct usbi_transfer *); + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int _cache_active_config_descriptor(struct libusb_device *, int); +static int _sync_control_transfer(struct usbi_transfer *); +static int _sync_gen_transfer(struct usbi_transfer *); +static int _access_endpoint(struct libusb_transfer *); + +const struct usbi_os_backend usbi_backend = { + .name = "Synchronous NetBSD backend", + .caps = 0, + .get_device_list = netbsd_get_device_list, + .open = netbsd_open, + .close = netbsd_close, + + .get_active_config_descriptor = netbsd_get_active_config_descriptor, + .get_config_descriptor = netbsd_get_config_descriptor, + + .get_configuration = netbsd_get_configuration, + .set_configuration = netbsd_set_configuration, + + .claim_interface = netbsd_claim_interface, + .release_interface = netbsd_release_interface, + + .set_interface_altsetting = netbsd_set_interface_altsetting, + .clear_halt = netbsd_clear_halt, + + .destroy_device = netbsd_destroy_device, + + .submit_transfer = netbsd_submit_transfer, + .cancel_transfer = netbsd_cancel_transfer, + + .handle_transfer_completion = netbsd_handle_transfer_completion, + + .device_priv_size = sizeof(struct device_priv), + .device_handle_priv_size = sizeof(struct handle_priv), +}; + +int +netbsd_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + struct libusb_device *dev; + struct device_priv *dpriv; + struct usb_device_info di; + usb_device_descriptor_t ddesc; + unsigned long session_id; + char devnode[16]; + int fd, err, i; + + usbi_dbg(ctx, " "); + + /* Only ugen(4) is supported */ + for (i = 0; i < USB_MAX_DEVICES; i++) { + /* Control endpoint is always .00 */ + snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i); + + if ((fd = open(devnode, O_RDONLY)) < 0) { + if (errno != ENOENT && errno != ENXIO) + usbi_err(ctx, "could not open %s", devnode); + continue; + } + + if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0) + continue; + + session_id = (di.udi_bus << 8 | di.udi_addr); + dev = usbi_get_device_by_session_id(ctx, session_id); + + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) + return LIBUSB_ERROR_NO_MEM; + + dev->bus_number = 1 + di.udi_bus; + dev->device_address = 1 + di.udi_addr; + dev->speed = di.udi_speed; /* NetBSD #define's happen to match libusb enum */ + + dpriv = usbi_get_device_priv(dev); + strlcpy(dpriv->devnode, devnode, sizeof(devnode)); + dpriv->fd = -1; + + if (ioctl(fd, USB_GET_DEVICE_DESC, &ddesc) < 0) { + err = errno; + goto error; + } + + static_assert(sizeof(dev->device_descriptor) == sizeof(ddesc), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, &ddesc, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + if (_cache_active_config_descriptor(dev, fd)) { + err = errno; + goto error; + } + + if ((err = usbi_sanitize_device(dev))) + goto error; + + usbi_connect_device(dev); + } + close(fd); + + if (discovered_devs_append(*discdevs, dev) == NULL) + return LIBUSB_ERROR_NO_MEM; + + libusb_unref_device(dev); + } + + return LIBUSB_SUCCESS; + +error: + close(fd); + libusb_unref_device(dev); + return _errno_to_libusb(err); +} + +int +netbsd_open(struct libusb_device_handle *handle) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + struct handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int i; + + dpriv->fd = open(dpriv->devnode, O_RDWR); + if (dpriv->fd < 0) { + dpriv->fd = open(dpriv->devnode, O_RDONLY); + if (dpriv->fd < 0) + return _errno_to_libusb(errno); + } + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + hpriv->endpoints[i] = -1; + + usbi_dbg(HANDLE_CTX(handle), "open %s: fd %d", dpriv->devnode, dpriv->fd); + + return LIBUSB_SUCCESS; +} + +void +netbsd_close(struct libusb_device_handle *handle) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + + usbi_dbg(HANDLE_CTX(handle), "close: fd %d", dpriv->fd); + + close(dpriv->fd); + dpriv->fd = -1; +} + +int +netbsd_get_active_config_descriptor(struct libusb_device *dev, + void *buf, size_t len) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + + len = MIN(len, (size_t)UGETW(dpriv->cdesc->wTotalLength)); + + usbi_dbg(DEVICE_CTX(dev), "len %zu", len); + + memcpy(buf, dpriv->cdesc, len); + + return (int)len; +} + +int +netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + void *buf, size_t len) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + struct usb_full_desc ufd; + int fd, err; + + 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) { + fd = dpriv->fd; + } else { + fd = open(dpriv->devnode, O_RDONLY); + if (fd < 0) + return _errno_to_libusb(errno); + } + + ufd.ufd_config_index = idx; + ufd.ufd_size = len; + ufd.ufd_data = buf; + + if (ioctl(fd, USB_GET_FULL_DESC, &ufd) < 0) { + err = errno; + if (dpriv->fd < 0) + close(fd); + return _errno_to_libusb(err); + } + + if (dpriv->fd < 0) + close(fd); + + return (int)len; +} + +int +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(HANDLE_CTX(handle), " "); + + if (ioctl(dpriv->fd, USB_GET_CONFIG, &tmp) < 0) + return _errno_to_libusb(errno); + + usbi_dbg(HANDLE_CTX(handle), "configuration %d", tmp); + *config = (uint8_t)tmp; + + return LIBUSB_SUCCESS; +} + +int +netbsd_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + + usbi_dbg(HANDLE_CTX(handle), "configuration %d", config); + + if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) + return _errno_to_libusb(errno); + + return _cache_active_config_descriptor(handle->dev, dpriv->fd); +} + +int +netbsd_claim_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + struct handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int i; + + UNUSED(iface); + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + hpriv->endpoints[i] = -1; + + return LIBUSB_SUCCESS; +} + +int +netbsd_release_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + struct handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int i; + + UNUSED(iface); + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + if (hpriv->endpoints[i] >= 0) + close(hpriv->endpoints[i]); + + return LIBUSB_SUCCESS; +} + +int +netbsd_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t iface, + uint8_t altsetting) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + struct usb_alt_interface intf; + + usbi_dbg(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting); + + memset(&intf, 0, sizeof(intf)); + + intf.uai_interface_index = iface; + intf.uai_alt_no = altsetting; + + if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) + return _errno_to_libusb(errno); + + return LIBUSB_SUCCESS; +} + +int +netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + struct usb_ctl_request req; + + usbi_dbg(HANDLE_CTX(handle), " "); + + req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; + req.ucr_request.bRequest = UR_CLEAR_FEATURE; + USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); + USETW(req.ucr_request.wIndex, endpoint); + USETW(req.ucr_request.wLength, 0); + + if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0) + return _errno_to_libusb(errno); + + return LIBUSB_SUCCESS; +} + +void +netbsd_destroy_device(struct libusb_device *dev) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + + usbi_dbg(DEVICE_CTX(dev), " "); + + free(dpriv->cdesc); +} + +int +netbsd_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + int err = 0; + + usbi_dbg(ITRANSFER_CTX(itransfer), " "); + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + err = _sync_control_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (IS_XFEROUT(transfer)) { + /* Isochronous write is not supported */ + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + if (err) + return err; + + usbi_signal_transfer_completion(itransfer); + + return LIBUSB_SUCCESS; +} + +int +netbsd_cancel_transfer(struct usbi_transfer *itransfer) +{ + UNUSED(itransfer); + + usbi_dbg(ITRANSFER_CTX(itransfer), " "); + + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +int +netbsd_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +_errno_to_libusb(int err) +{ + usbi_dbg(NULL, "error: %s (%d)", strerror(err), err); + + switch (err) { + case EIO: + return LIBUSB_ERROR_IO; + case EACCES: + return LIBUSB_ERROR_ACCESS; + case ENOENT: + return LIBUSB_ERROR_NO_DEVICE; + case ENOMEM: + return LIBUSB_ERROR_NO_MEM; + case EWOULDBLOCK: + case ETIMEDOUT: + return LIBUSB_ERROR_TIMEOUT; + default: + return LIBUSB_ERROR_OTHER; + } +} + +int +_cache_active_config_descriptor(struct libusb_device *dev, int fd) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + struct usb_config_desc ucd; + struct usb_full_desc ufd; + void *buf; + int len; + + 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(DEVICE_CTX(dev), "active bLength %d", ucd.ucd_desc.bLength); + + len = UGETW(ucd.ucd_desc.wTotalLength); + buf = malloc((size_t)len); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + ufd.ufd_config_index = ucd.ucd_config_index; + ufd.ufd_size = len; + ufd.ufd_data = buf; + + usbi_dbg(DEVICE_CTX(dev), "index %d, len %d", ufd.ufd_config_index, len); + + if (ioctl(fd, USB_GET_FULL_DESC, &ufd) < 0) { + free(buf); + return _errno_to_libusb(errno); + } + + if (dpriv->cdesc) + free(dpriv->cdesc); + dpriv->cdesc = buf; + + return 0; +} + +int +_sync_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_control_setup *setup; + struct device_priv *dpriv; + struct usb_ctl_request req; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = usbi_get_device_priv(transfer->dev_handle->dev); + setup = (struct libusb_control_setup *)transfer->buffer; + + 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), + libusb_le16_to_cpu(setup->wLength), transfer->timeout); + + req.ucr_request.bmRequestType = setup->bmRequestType; + req.ucr_request.bRequest = setup->bRequest; + /* Don't use USETW, libusb already deals with the endianness */ + (*(uint16_t *)req.ucr_request.wValue) = setup->wValue; + (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; + (*(uint16_t *)req.ucr_request.wLength) = setup->wLength; + req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + req.ucr_flags = USBD_SHORT_XFER_OK; + + if (ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout) < 0) + return _errno_to_libusb(errno); + + if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = req.ucr_actlen; + + usbi_dbg(ITRANSFER_CTX(itransfer), "transferred %d", itransfer->transferred); + + return 0; +} + +int +_access_endpoint(struct libusb_transfer *transfer) +{ + struct handle_priv *hpriv; + struct device_priv *dpriv; + char *s, devnode[16]; + int fd, endpt; + mode_t mode; + + hpriv = usbi_get_device_handle_priv(transfer->dev_handle); + dpriv = usbi_get_device_priv(transfer->dev_handle->dev); + + endpt = UE_GET_ADDR(transfer->endpoint); + mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; + + usbi_dbg(TRANSFER_CTX(transfer), "endpoint %d mode %d", endpt, mode); + + if (hpriv->endpoints[endpt] < 0) { + /* Pick the right node given the control one */ + strlcpy(devnode, dpriv->devnode, sizeof(devnode)); + s = strchr(devnode, '.'); + snprintf(s, 4, ".%02d", endpt); + + /* We may need to read/write to the same endpoint later. */ + if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) + if ((fd = open(devnode, mode)) < 0) + return -1; + + hpriv->endpoints[endpt] = fd; + } + + return hpriv->endpoints[endpt]; +} + +int +_sync_gen_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + int fd, nr = 1; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + /* + * Bulk, Interrupt or Isochronous transfer depends on the + * endpoint and thus the node to open. + */ + if ((fd = _access_endpoint(transfer)) < 0) + return _errno_to_libusb(errno); + + if (ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout) < 0) + return _errno_to_libusb(errno); + + if (IS_XFERIN(transfer)) { + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + if (ioctl(fd, USB_SET_SHORT_XFER, &nr) < 0) + return _errno_to_libusb(errno); + + nr = read(fd, transfer->buffer, transfer->length); + } else { + nr = write(fd, transfer->buffer, transfer->length); + } + + if (nr < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = nr; + + return 0; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/null_usb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/null_usb.c new file mode 100644 index 00000000..0cd531d7 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/null_usb.c @@ -0,0 +1,111 @@ +/* + * Copyright © 2019 Pino Toscano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +static int +null_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + return LIBUSB_SUCCESS; +} + +static int +null_open(struct libusb_device_handle *handle) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void +null_close(struct libusb_device_handle *handle) +{ +} + +static int +null_get_active_config_descriptor(struct libusb_device *dev, + void *buf, size_t len) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + void *buf, size_t len) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_set_configuration(struct libusb_device_handle *handle, int config) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_claim_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_release_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t iface, + uint8_t altsetting) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_submit_transfer(struct usbi_transfer *itransfer) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int +null_cancel_transfer(struct usbi_transfer *itransfer) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +const struct usbi_os_backend usbi_backend = { + .name = "Null backend", + .caps = 0, + .get_device_list = null_get_device_list, + .open = null_open, + .close = null_close, + .get_active_config_descriptor = null_get_active_config_descriptor, + .get_config_descriptor = null_get_config_descriptor, + .set_configuration = null_set_configuration, + .claim_interface = null_claim_interface, + .release_interface = null_release_interface, + .set_interface_altsetting = null_set_interface_altsetting, + .clear_halt = null_clear_halt, + .submit_transfer = null_submit_transfer, + .cancel_transfer = null_cancel_transfer, +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/openbsd_usb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/openbsd_usb.c new file mode 100644 index 00000000..95ed6e86 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/openbsd_usb.c @@ -0,0 +1,702 @@ +/* + * Copyright © 2011-2013 Martin Pieuchot + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "libusbi.h" + +struct device_priv { + char *devname; /* name of the ugen(4) node */ + int fd; /* device file descriptor */ + + usb_config_descriptor_t *cdesc; /* active config descriptor */ +}; + +struct handle_priv { + int endpoints[USB_MAX_ENDPOINTS]; +}; + +/* + * Backend functions + */ +static int obsd_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int obsd_open(struct libusb_device_handle *); +static void obsd_close(struct libusb_device_handle *); + +static int obsd_get_active_config_descriptor(struct libusb_device *, + void *, size_t); +static int obsd_get_config_descriptor(struct libusb_device *, uint8_t, + void *, size_t); + +static int obsd_get_configuration(struct libusb_device_handle *, uint8_t *); +static int obsd_set_configuration(struct libusb_device_handle *, int); + +static int obsd_claim_interface(struct libusb_device_handle *, uint8_t); +static int obsd_release_interface(struct libusb_device_handle *, uint8_t); + +static int obsd_set_interface_altsetting(struct libusb_device_handle *, uint8_t, + uint8_t); +static int obsd_clear_halt(struct libusb_device_handle *, unsigned char); +static void obsd_destroy_device(struct libusb_device *); + +static int obsd_submit_transfer(struct usbi_transfer *); +static int obsd_cancel_transfer(struct usbi_transfer *); +static int obsd_handle_transfer_completion(struct usbi_transfer *); + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int _cache_active_config_descriptor(struct libusb_device *); +static int _sync_control_transfer(struct usbi_transfer *); +static int _sync_gen_transfer(struct usbi_transfer *); +static int _access_endpoint(struct libusb_transfer *); + +static int _bus_open(int); + + +const struct usbi_os_backend usbi_backend = { + .name = "Synchronous OpenBSD backend", + .get_device_list = obsd_get_device_list, + .open = obsd_open, + .close = obsd_close, + + .get_active_config_descriptor = obsd_get_active_config_descriptor, + .get_config_descriptor = obsd_get_config_descriptor, + + .get_configuration = obsd_get_configuration, + .set_configuration = obsd_set_configuration, + + .claim_interface = obsd_claim_interface, + .release_interface = obsd_release_interface, + + .set_interface_altsetting = obsd_set_interface_altsetting, + .clear_halt = obsd_clear_halt, + .destroy_device = obsd_destroy_device, + + .submit_transfer = obsd_submit_transfer, + .cancel_transfer = obsd_cancel_transfer, + + .handle_transfer_completion = obsd_handle_transfer_completion, + + .device_priv_size = sizeof(struct device_priv), + .device_handle_priv_size = sizeof(struct handle_priv), +}; + +#define DEVPATH "/dev/" +#define USBDEV DEVPATH "usb" + +int +obsd_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + struct discovered_devs *ddd; + struct libusb_device *dev; + struct device_priv *dpriv; + struct usb_device_info di; + struct usb_device_ddesc dd; + unsigned long session_id; + char devices[USB_MAX_DEVICES]; + char busnode[16]; + char *udevname; + int fd, addr, i, j; + + usbi_dbg(ctx, " "); + + for (i = 0; i < 8; i++) { + snprintf(busnode, sizeof(busnode), USBDEV "%d", i); + + if ((fd = open(busnode, O_RDWR)) < 0) { + if (errno != ENOENT && errno != ENXIO) + usbi_err(ctx, "could not open %s", busnode); + continue; + } + + bzero(devices, sizeof(devices)); + for (addr = 1; addr < USB_MAX_DEVICES; addr++) { + if (devices[addr]) + continue; + + di.udi_addr = addr; + if (ioctl(fd, USB_DEVICEINFO, &di) < 0) + continue; + + /* + * XXX If ugen(4) is attached to the USB device + * it will be used. + */ + udevname = NULL; + for (j = 0; j < USB_MAX_DEVNAMES; j++) + if (!strncmp("ugen", di.udi_devnames[j], 4)) { + udevname = strdup(di.udi_devnames[j]); + break; + } + + session_id = (di.udi_bus << 8 | di.udi_addr); + dev = usbi_get_device_by_session_id(ctx, session_id); + + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + close(fd); + return LIBUSB_ERROR_NO_MEM; + } + + dev->bus_number = di.udi_bus; + dev->device_address = di.udi_addr; + dev->speed = di.udi_speed; + dev->port_number = di.udi_port; + + dpriv = usbi_get_device_priv(dev); + dpriv->fd = -1; + dpriv->devname = udevname; + + dd.udd_bus = di.udi_bus; + dd.udd_addr = di.udi_addr; + if (ioctl(fd, USB_DEVICE_GET_DDESC, &dd) < 0) { + libusb_unref_device(dev); + continue; + } + + static_assert(sizeof(dev->device_descriptor) == sizeof(dd.udd_desc), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, &dd.udd_desc, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + if (_cache_active_config_descriptor(dev)) { + libusb_unref_device(dev); + continue; + } + + if (usbi_sanitize_device(dev)) { + libusb_unref_device(dev); + continue; + } + + usbi_connect_device(dev); + } + + ddd = discovered_devs_append(*discdevs, dev); + if (ddd == NULL) { + close(fd); + return LIBUSB_ERROR_NO_MEM; + } + libusb_unref_device(dev); + + *discdevs = ddd; + devices[addr] = 1; + } + + close(fd); + } + + return LIBUSB_SUCCESS; +} + +int +obsd_open(struct libusb_device_handle *handle) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + char devnode[16]; + + if (dpriv->devname) { + int fd; + /* + * Only open ugen(4) attached devices read-write, all + * read-only operations are done through the bus node. + */ + snprintf(devnode, sizeof(devnode), DEVPATH "%s.00", + dpriv->devname); + fd = open(devnode, O_RDWR); + if (fd < 0) + return _errno_to_libusb(errno); + dpriv->fd = fd; + + usbi_dbg(HANDLE_CTX(handle), "open %s: fd %d", devnode, dpriv->fd); + } + + return LIBUSB_SUCCESS; +} + +void +obsd_close(struct libusb_device_handle *handle) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + + if (dpriv->devname) { + usbi_dbg(HANDLE_CTX(handle), "close: fd %d", dpriv->fd); + + close(dpriv->fd); + dpriv->fd = -1; + } +} + +int +obsd_get_active_config_descriptor(struct libusb_device *dev, + void *buf, size_t len) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + + len = MIN(len, (size_t)UGETW(dpriv->cdesc->wTotalLength)); + + usbi_dbg(DEVICE_CTX(dev), "len %zu", len); + + memcpy(buf, dpriv->cdesc, len); + + return (int)len; +} + +int +obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + void *buf, size_t len) +{ + struct usb_device_fdesc udf; + int fd, err; + + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + udf.udf_bus = dev->bus_number; + udf.udf_addr = dev->device_address; + udf.udf_config_index = idx; + udf.udf_size = len; + udf.udf_data = buf; + + 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; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + + return (int)len; +} + +int +obsd_get_configuration(struct libusb_device_handle *handle, uint8_t *config) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + + *config = dpriv->cdesc->bConfigurationValue; + + usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %u", *config); + + return LIBUSB_SUCCESS; +} + +int +obsd_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + + if (dpriv->devname == NULL) + return LIBUSB_ERROR_NOT_SUPPORTED; + + usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %d", config); + + if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) + return _errno_to_libusb(errno); + + return _cache_active_config_descriptor(handle->dev); +} + +int +obsd_claim_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + struct handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int i; + + UNUSED(iface); + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + hpriv->endpoints[i] = -1; + + return LIBUSB_SUCCESS; +} + +int +obsd_release_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + struct handle_priv *hpriv = usbi_get_device_handle_priv(handle); + int i; + + UNUSED(iface); + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + if (hpriv->endpoints[i] >= 0) + close(hpriv->endpoints[i]); + + return LIBUSB_SUCCESS; +} + +int +obsd_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t iface, + uint8_t altsetting) +{ + struct device_priv *dpriv = usbi_get_device_priv(handle->dev); + struct usb_alt_interface intf; + + if (dpriv->devname == NULL) + return LIBUSB_ERROR_NOT_SUPPORTED; + + usbi_dbg(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting); + + memset(&intf, 0, sizeof(intf)); + + intf.uai_interface_index = iface; + intf.uai_alt_no = altsetting; + + if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) + return _errno_to_libusb(errno); + + return LIBUSB_SUCCESS; +} + +int +obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + struct usb_ctl_request req; + int fd, err; + + if ((fd = _bus_open(handle->dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + usbi_dbg(HANDLE_CTX(handle), " "); + + req.ucr_addr = handle->dev->device_address; + req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; + req.ucr_request.bRequest = UR_CLEAR_FEATURE; + USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); + USETW(req.ucr_request.wIndex, endpoint); + USETW(req.ucr_request.wLength, 0); + + if (ioctl(fd, USB_REQUEST, &req) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + + return LIBUSB_SUCCESS; +} + +void +obsd_destroy_device(struct libusb_device *dev) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + + usbi_dbg(DEVICE_CTX(dev), " "); + + free(dpriv->cdesc); + free(dpriv->devname); +} + +int +obsd_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + int err = 0; + + usbi_dbg(ITRANSFER_CTX(itransfer), " "); + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + err = _sync_control_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (IS_XFEROUT(transfer)) { + /* Isochronous write is not supported */ + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + if (err) + return err; + + usbi_signal_transfer_completion(itransfer); + + return LIBUSB_SUCCESS; +} + +int +obsd_cancel_transfer(struct usbi_transfer *itransfer) +{ + UNUSED(itransfer); + + usbi_dbg(ITRANSFER_CTX(itransfer), " "); + + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +int +obsd_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +_errno_to_libusb(int err) +{ + usbi_dbg(NULL, "error: %s (%d)", strerror(err), err); + + switch (err) { + case EIO: + return LIBUSB_ERROR_IO; + case EACCES: + return LIBUSB_ERROR_ACCESS; + case ENOENT: + return LIBUSB_ERROR_NO_DEVICE; + case ENOMEM: + return LIBUSB_ERROR_NO_MEM; + case ETIMEDOUT: + return LIBUSB_ERROR_TIMEOUT; + default: + return LIBUSB_ERROR_OTHER; + } +} + +int +_cache_active_config_descriptor(struct libusb_device *dev) +{ + struct device_priv *dpriv = usbi_get_device_priv(dev); + struct usb_device_cdesc udc; + struct usb_device_fdesc udf; + void *buf; + int fd, len, err; + + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + 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; + udc.udc_config_index = USB_CURRENT_CONFIG_INDEX; + if (ioctl(fd, USB_DEVICE_GET_CDESC, &udc) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(errno); + } + + usbi_dbg(DEVICE_CTX(dev), "active bLength %d", udc.udc_desc.bLength); + + len = UGETW(udc.udc_desc.wTotalLength); + buf = malloc((size_t)len); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + udf.udf_bus = dev->bus_number; + udf.udf_addr = dev->device_address; + udf.udf_config_index = udc.udc_config_index; + udf.udf_size = len; + udf.udf_data = buf; + + usbi_dbg(DEVICE_CTX(dev), "index %d, len %d", udf.udf_config_index, len); + + if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { + err = errno; + close(fd); + free(buf); + return _errno_to_libusb(err); + } + close(fd); + + if (dpriv->cdesc) + free(dpriv->cdesc); + dpriv->cdesc = buf; + + return LIBUSB_SUCCESS; +} + +int +_sync_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_control_setup *setup; + struct device_priv *dpriv; + struct usb_ctl_request req; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = usbi_get_device_priv(transfer->dev_handle->dev); + setup = (struct libusb_control_setup *)transfer->buffer; + + 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), + libusb_le16_to_cpu(setup->wLength), transfer->timeout); + + req.ucr_addr = transfer->dev_handle->dev->device_address; + req.ucr_request.bmRequestType = setup->bmRequestType; + req.ucr_request.bRequest = setup->bRequest; + /* Don't use USETW, libusb already deals with the endianness */ + (*(uint16_t *)req.ucr_request.wValue) = setup->wValue; + (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; + (*(uint16_t *)req.ucr_request.wLength) = setup->wLength; + req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + req.ucr_flags = USBD_SHORT_XFER_OK; + + if (dpriv->devname == NULL) { + /* + * XXX If the device is not attached to ugen(4) it is + * XXX still possible to submit a control transfer but + * XXX with the default timeout only. + */ + int fd, err; + + if ((fd = _bus_open(transfer->dev_handle->dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + if (ioctl(fd, USB_REQUEST, &req) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + } else { + if (ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout) < 0) + return _errno_to_libusb(errno); + + if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0) + return _errno_to_libusb(errno); + } + + itransfer->transferred = req.ucr_actlen; + + usbi_dbg(ITRANSFER_CTX(itransfer), "transferred %d", itransfer->transferred); + + return 0; +} + +int +_access_endpoint(struct libusb_transfer *transfer) +{ + struct handle_priv *hpriv; + struct device_priv *dpriv; + char devnode[16]; + int fd, endpt; + mode_t mode; + + hpriv = usbi_get_device_handle_priv(transfer->dev_handle); + dpriv = usbi_get_device_priv(transfer->dev_handle->dev); + + endpt = UE_GET_ADDR(transfer->endpoint); + mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; + + usbi_dbg(TRANSFER_CTX(transfer), "endpoint %d mode %d", endpt, mode); + + if (hpriv->endpoints[endpt] < 0) { + /* Pick the right endpoint node */ + snprintf(devnode, sizeof(devnode), DEVPATH "%s.%02d", + dpriv->devname, endpt); + + /* We may need to read/write to the same endpoint later. */ + if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) + if ((fd = open(devnode, mode)) < 0) + return -1; + + hpriv->endpoints[endpt] = fd; + } + + return hpriv->endpoints[endpt]; +} + +int +_sync_gen_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct device_priv *dpriv; + int fd, nr = 1; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = usbi_get_device_priv(transfer->dev_handle->dev); + + if (dpriv->devname == NULL) + return LIBUSB_ERROR_NOT_SUPPORTED; + + /* + * Bulk, Interrupt or Isochronous transfer depends on the + * endpoint and thus the node to open. + */ + if ((fd = _access_endpoint(transfer)) < 0) + return _errno_to_libusb(errno); + + if (ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout) < 0) + return _errno_to_libusb(errno); + + if (IS_XFERIN(transfer)) { + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + if (ioctl(fd, USB_SET_SHORT_XFER, &nr) < 0) + return _errno_to_libusb(errno); + + nr = read(fd, transfer->buffer, transfer->length); + } else { + nr = write(fd, transfer->buffer, transfer->length); + } + + if (nr < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = nr; + + return 0; +} + +int +_bus_open(int number) +{ + char busnode[16]; + + snprintf(busnode, sizeof(busnode), USBDEV "%d", number); + + return open(busnode, O_RDWR); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.c new file mode 100644 index 00000000..75df621d --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.c @@ -0,0 +1,1621 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. + * Copyright 2023 Oxide Computer Company + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "sunos_usb.h" + +#define UPDATEDRV_PATH "/usr/sbin/update_drv" +#define UPDATEDRV "update_drv" + +#define DEFAULT_LISTSIZE 6 + +typedef struct { + int nargs; + int listsize; + char **string; +} string_list_t; + +/* + * Backend functions + */ +static int sunos_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int sunos_open(struct libusb_device_handle *); +static void sunos_close(struct libusb_device_handle *); +static int sunos_get_active_config_descriptor(struct libusb_device *, + void *, size_t); +static int sunos_get_config_descriptor(struct libusb_device *, uint8_t, + void *, size_t); +static int sunos_get_configuration(struct libusb_device_handle *, uint8_t *); +static int sunos_set_configuration(struct libusb_device_handle *, int); +static int sunos_claim_interface(struct libusb_device_handle *, uint8_t); +static int sunos_release_interface(struct libusb_device_handle *, uint8_t); +static int sunos_set_interface_altsetting(struct libusb_device_handle *, + uint8_t, uint8_t); +static int sunos_clear_halt(struct libusb_device_handle *, unsigned char); +static void sunos_destroy_device(struct libusb_device *); +static int sunos_submit_transfer(struct usbi_transfer *); +static int sunos_cancel_transfer(struct usbi_transfer *); +static int sunos_handle_transfer_completion(struct usbi_transfer *); +static int sunos_kernel_driver_active(struct libusb_device_handle *, uint8_t); +static int sunos_detach_kernel_driver(struct libusb_device_handle *, uint8_t); +static int sunos_attach_kernel_driver(struct libusb_device_handle *, uint8_t); +static int sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv); +static int sunos_usb_ioctl(struct libusb_device *dev, int cmd); + +static int sunos_get_link(di_devlink_t devlink, void *arg) +{ + walk_link_t *link_arg = (walk_link_t *)arg; + const char *p; + const char *q; + + if (link_arg->path) { + char *content = (char *)di_devlink_content(devlink); + char *start = strstr(content, "/devices/"); + start += strlen("/devices"); + usbi_dbg(NULL, "%s", start); + + /* line content must have minor node */ + if (start == NULL || + strncmp(start, link_arg->path, link_arg->len) != 0 || + start[link_arg->len] != ':') + return (DI_WALK_CONTINUE); + } + + p = di_devlink_path(devlink); + q = strrchr(p, '/'); + usbi_dbg(NULL, "%s", q); + + *(link_arg->linkpp) = strndup(p, strlen(p) - strlen(q)); + + return (DI_WALK_TERMINATE); +} + + +static int sunos_physpath_to_devlink( + const char *node_path, const char *match, char **link_path) +{ + walk_link_t link_arg; + di_devlink_handle_t hdl; + + *link_path = NULL; + link_arg.linkpp = link_path; + if ((hdl = di_devlink_init(NULL, 0)) == NULL) { + usbi_dbg(NULL, "di_devlink_init failure"); + return (-1); + } + + link_arg.len = strlen(node_path); + link_arg.path = (char *)node_path; + + (void) di_devlink_walk(hdl, match, NULL, DI_PRIMARY_LINK, + (void *)&link_arg, sunos_get_link); + + (void) di_devlink_fini(&hdl); + + if (*link_path == NULL) { + usbi_dbg(NULL, "there is no devlink for this path"); + return (-1); + } + + return 0; +} + +static int +sunos_usb_ioctl(struct libusb_device *dev, int cmd) +{ + int fd; + nvlist_t *nvlist; + char *end; + char *phypath; + char *hubpath; + char path_arg[PATH_MAX]; + sunos_dev_priv_t *dpriv; + devctl_ap_state_t devctl_ap_state; + struct devctl_iocdata iocdata; + + dpriv = usbi_get_device_priv(dev); + phypath = dpriv->phypath; + + end = strrchr(phypath, '/'); + if (end == NULL) + return (-1); + hubpath = strndup(phypath, end - phypath); + if (hubpath == NULL) + return (-1); + + end = strrchr(hubpath, '@'); + if (end == NULL) { + free(hubpath); + return (-1); + } + 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(DEVICE_CTX(dev), "ioctl hub path: %s", path_arg); + + fd = open(path_arg, O_RDONLY); + if (fd < 0) { + usbi_err(DEVICE_CTX(dev), "open failed: errno %d (%s)", errno, strerror(errno)); + nvlist_free(nvlist); + free(hubpath); + return (-1); + } + + memset(&iocdata, 0, sizeof(iocdata)); + memset(&devctl_ap_state, 0, sizeof(devctl_ap_state)); + + nvlist_pack(nvlist, (char **)&iocdata.nvl_user, &iocdata.nvl_usersz, NV_ENCODE_NATIVE, 0); + + iocdata.cmd = DEVCTL_AP_GETSTATE; + iocdata.flags = 0; + iocdata.c_nodename = (char *)"hub"; + iocdata.c_unitaddr = end; + iocdata.cpyout_buf = &devctl_ap_state; + 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(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; + iocdata.cmd = cmd; + if (ioctl(fd, (int)cmd, &iocdata) != 0) { + usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)", + fd, cmd, errno, strerror(errno)); + sleep(2); + } + + close(fd); + free(iocdata.nvl_user); + nvlist_free(nvlist); + free(hubpath); + + return (-errno); +} + +static int +sunos_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) +{ + sunos_dev_priv_t *dpriv = usbi_get_device_priv(dev_handle->dev); + + UNUSED(interface); + + usbi_dbg(HANDLE_CTX(dev_handle), "%s", dpriv->ugenpath); + + return (dpriv->ugenpath == NULL); +} + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int sunos_usb_get_status(struct libusb_context *ctx, int fd); + +static string_list_t * +sunos_new_string_list(void) +{ + string_list_t *list; + + list = calloc(1, sizeof(string_list_t)); + if (list == NULL) + return (NULL); + list->string = calloc(DEFAULT_LISTSIZE, sizeof(char *)); + if (list->string == NULL) { + free(list); + return (NULL); + } + list->nargs = 0; + list->listsize = DEFAULT_LISTSIZE; + + return (list); +} + +static int +sunos_append_to_string_list(string_list_t *list, const char *arg) +{ + char *str = strdup(arg); + + if (str == NULL) + return (-1); + + if ((list->nargs + 1) == list->listsize) { /* +1 is for NULL */ + char **tmp = realloc(list->string, + sizeof(char *) * (list->listsize + 1)); + if (tmp == NULL) { + free(str); + return (-1); + } + list->string = tmp; + list->string[list->listsize++] = NULL; + } + list->string[list->nargs++] = str; + + return (0); +} + +static void +sunos_free_string_list(string_list_t *list) +{ + int i; + + for (i = 0; i < list->nargs; i++) { + free(list->string[i]); + } + + free(list->string); + free(list); +} + +static char ** +sunos_build_argv_list(string_list_t *list) +{ + return (list->string); +} + + +static int +sunos_exec_command(struct libusb_context *ctx, const char *path, + string_list_t *list) +{ + pid_t pid; + int status; + int waitstat; + int exit_status; + char **argv_list; + + argv_list = sunos_build_argv_list(list); + if (argv_list == NULL) + return (-1); + + pid = fork(); + if (pid == 0) { + /* child */ + execv(path, argv_list); + _exit(127); + } else if (pid > 0) { + /* parent */ + do { + waitstat = waitpid(pid, &status, 0); + } while ((waitstat == -1 && errno == EINTR) || + (waitstat == 0 && !WIFEXITED(status) && !WIFSIGNALED(status))); + + if (waitstat == 0) { + if (WIFEXITED(status)) + exit_status = WEXITSTATUS(status); + else + exit_status = WTERMSIG(status); + } else { + usbi_err(ctx, "waitpid failed: errno %d (%s)", errno, strerror(errno)); + exit_status = -1; + } + } else { + /* fork failed */ + usbi_err(ctx, "fork failed: errno %d (%s)", errno, strerror(errno)); + exit_status = -1; + } + + return (exit_status); +} + +static int +sunos_detach_kernel_driver(struct libusb_device_handle *dev_handle, + uint8_t interface_number) +{ + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + string_list_t *list; + char path_arg[PATH_MAX]; + sunos_dev_priv_t *dpriv; + int r; + + UNUSED(interface_number); + + dpriv = usbi_get_device_priv(dev_handle->dev); + snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath); + usbi_dbg(HANDLE_CTX(dev_handle), "%s", path_arg); + + list = sunos_new_string_list(); + if (list == NULL) + return (LIBUSB_ERROR_NO_MEM); + + /* attach ugen driver */ + r = 0; + r |= sunos_append_to_string_list(list, UPDATEDRV); + r |= sunos_append_to_string_list(list, "-a"); /* add rule */ + r |= sunos_append_to_string_list(list, "-i"); /* specific device */ + r |= sunos_append_to_string_list(list, path_arg); /* physical path */ + r |= sunos_append_to_string_list(list, "ugen"); + if (r) { + sunos_free_string_list(list); + return (LIBUSB_ERROR_NO_MEM); + } + + r = sunos_exec_command(ctx, UPDATEDRV_PATH, list); + sunos_free_string_list(list); + if (r < 0) + return (LIBUSB_ERROR_OTHER); + + /* reconfigure the driver node */ + r = 0; + r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT); + r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE); + if (r) + usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed"); + + snprintf(path_arg, sizeof(path_arg), "^usb/%x.%x", + dev_handle->dev->device_descriptor.idVendor, + dev_handle->dev->device_descriptor.idProduct); + sunos_physpath_to_devlink(dpriv->phypath, path_arg, &dpriv->ugenpath); + + if (access(dpriv->ugenpath, F_OK) == -1) { + usbi_err(HANDLE_CTX(dev_handle), "fail to detach kernel driver"); + return (LIBUSB_ERROR_IO); + } + + return sunos_usb_open_ep0(usbi_get_device_handle_priv(dev_handle), dpriv); +} + +static int +sunos_attach_kernel_driver(struct libusb_device_handle *dev_handle, + uint8_t interface_number) +{ + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + string_list_t *list; + char path_arg[PATH_MAX]; + sunos_dev_priv_t *dpriv; + int r; + + UNUSED(interface_number); + + /* we open the dev in detach driver, so we need close it first. */ + sunos_close(dev_handle); + + dpriv = usbi_get_device_priv(dev_handle->dev); + snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath); + usbi_dbg(HANDLE_CTX(dev_handle), "%s", path_arg); + + list = sunos_new_string_list(); + if (list == NULL) + return (LIBUSB_ERROR_NO_MEM); + + /* detach ugen driver */ + r = 0; + r |= sunos_append_to_string_list(list, UPDATEDRV); + r |= sunos_append_to_string_list(list, "-d"); /* add rule */ + r |= sunos_append_to_string_list(list, "-i"); /* specific device */ + r |= sunos_append_to_string_list(list, path_arg); /* physical path */ + r |= sunos_append_to_string_list(list, "ugen"); + if (r) { + sunos_free_string_list(list); + return (LIBUSB_ERROR_NO_MEM); + } + + r = sunos_exec_command(ctx, UPDATEDRV_PATH, list); + sunos_free_string_list(list); + if (r < 0) + return (LIBUSB_ERROR_OTHER); + + /* reconfigure the driver node */ + r = 0; + r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE); + r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT); + r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE); + if (r) + usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed"); + + return 0; +} + +static int +sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev) +{ + int proplen; + int *i, n, *addr, *port_prop; + char *phypath; + uint8_t *rdata; + sunos_dev_priv_t *dpriv = usbi_get_device_priv(dev); + char match_str[PATH_MAX]; + + /* Device descriptors */ + proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "usb-dev-descriptor", &rdata); + if (proplen <= 0) { + return (LIBUSB_ERROR_IO); + } + bcopy(rdata, &dev->device_descriptor, LIBUSB_DT_DEVICE_SIZE); + + /* Raw configuration descriptors */ + proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "usb-raw-cfg-descriptors", &rdata); + if (proplen <= 0) { + usbi_dbg(DEVICE_CTX(dev), "can't find raw config descriptors"); + + return (LIBUSB_ERROR_IO); + } + dpriv->raw_cfgdescr = calloc(1, proplen); + if (dpriv->raw_cfgdescr == NULL) { + return (LIBUSB_ERROR_NO_MEM); + } else { + bcopy(rdata, dpriv->raw_cfgdescr, proplen); + dpriv->cfgvalue = ((struct libusb_config_descriptor *) + rdata)->bConfigurationValue; + } + + n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &port_prop); + + if ((n != 1) || (*port_prop <= 0)) { + return (LIBUSB_ERROR_IO); + } + dev->port_number = *port_prop; + + /* device physical path */ + phypath = di_devfs_path(node); + if (phypath) { + dpriv->phypath = strdup(phypath); + snprintf(match_str, sizeof(match_str), "^usb/%x.%x", + dev->device_descriptor.idVendor, + dev->device_descriptor.idProduct); + 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); + + } else { + free(dpriv->raw_cfgdescr); + + return (LIBUSB_ERROR_IO); + } + + /* address */ + n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "assigned-address", &addr); + if (n != 1 || *addr == 0) { + usbi_dbg(DEVICE_CTX(dev), "can't get address"); + } else { + dev->device_address = *addr; + } + + /* speed */ + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) { + dev->speed = LIBUSB_SPEED_LOW; + } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) { + dev->speed = LIBUSB_SPEED_HIGH; + } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "full-speed", &i) >= 0) { + dev->speed = LIBUSB_SPEED_FULL; + } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "super-speed", &i) >= 0) { + dev->speed = LIBUSB_SPEED_SUPER; + } + + 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); + + return (LIBUSB_SUCCESS); +} + +static int +sunos_add_devices(di_devlink_t link, void *arg) +{ + struct devlink_cbarg *largs = (struct devlink_cbarg *)arg; + struct node_args *nargs; + di_node_t myself, dn; + uint64_t session_id = 0; + uint64_t sid = 0; + uint64_t bdf = 0; + struct libusb_device *dev; + sunos_dev_priv_t *devpriv; + int n, *j; + int i = 0; + int *addr_prop; + uint8_t bus_number = 0; + uint32_t * regbuf = NULL; + uint32_t reg; + + UNUSED(link); + + nargs = (struct node_args *)largs->nargs; + myself = largs->myself; + + /* + * Construct session ID. + * session ID = dev_addr | hub addr |parent hub addr|...|root hub bdf + * 8 bits 8bits 8 bits 16bits + */ + if (myself == DI_NODE_NIL) + return (DI_WALK_CONTINUE); + + dn = myself; + /* find the root hub */ + while (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "root-hub", &j) != 0) { + 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); + dn = di_parent_node(dn); + } + + /* dn is the root hub node */ + n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "reg", (int **)®buf); + reg = regbuf[0]; + bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); + /* bdf must larger than i*8 bits */ + session_id |= (bdf << i * 8); + bus_number = (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); + + usbi_dbg(NULL, "device bus address=%s:%x, name:%s", + di_bus_addr(myself), bus_number, di_node_name(dn)); + 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(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; + /* usb_addr */ + n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, + "assigned-address", &addr_prop); + if ((n != 1) || (addr_prop[0] == 0)) { + usbi_dbg(NULL, "cannot get valid usb_addr"); + continue; + } + + sid = (session_id << 8) | (addr_prop[0] & 0xff) ; + 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(NULL, "can't alloc device"); + continue; + } + devpriv = usbi_get_device_priv(dev); + dev->bus_number = bus_number; + + if (sunos_fill_in_dev_info(dn, dev) != LIBUSB_SUCCESS) { + libusb_unref_device(dev); + usbi_dbg(NULL, "get information fail"); + continue; + } + if (usbi_sanitize_device(dev) < 0) { + libusb_unref_device(dev); + usbi_dbg(NULL, "sanitize failed: "); + return (DI_WALK_TERMINATE); + } + + usbi_connect_device(dev); + } else { + devpriv = usbi_get_device_priv(dev); + usbi_dbg(NULL, "Dev %s exists", devpriv->ugenpath); + } + + if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) { + usbi_dbg(NULL, "cannot append device"); + } + + /* + * we alloc and hence ref this dev. We don't need to ref it + * hereafter. Front end or app should take care of their ref. + */ + libusb_unref_device(dev); + + 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); + } + + return (DI_WALK_CONTINUE); +} + +static int +sunos_walk_minor_node_link(di_node_t node, void *args) +{ + di_minor_t minor = DI_MINOR_NIL; + char *minor_path; + struct devlink_cbarg arg; + struct node_args *nargs = (struct node_args *)args; + di_devlink_handle_t devlink_hdl = nargs->dlink_hdl; + + /* walk each minor to find usb devices */ + while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { + minor_path = di_devfs_minor_path(minor); + arg.nargs = args; + arg.myself = node; + arg.minor = minor; + (void) di_devlink_walk(devlink_hdl, + "^usb/hub[0-9]+", minor_path, + DI_PRIMARY_LINK, (void *)&arg, sunos_add_devices); + di_devfs_path_free(minor_path); + } + + /* switch to a different node */ + nargs->last_ugenpath = NULL; + + return (DI_WALK_CONTINUE); +} + +int +sunos_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + di_node_t root_node; + struct node_args args; + di_devlink_handle_t devlink_hdl; + + args.ctx = ctx; + args.discdevs = discdevs; + args.last_ugenpath = NULL; + if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + 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(ctx, "di_devlink_init() failed: errno %d (%s)", errno, strerror(errno)); + + return (LIBUSB_ERROR_IO); + } + args.dlink_hdl = devlink_hdl; + + /* 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(ctx, "di_walk_node() failed: errno %d (%s)", errno, strerror(errno)); + di_fini(root_node); + + return (LIBUSB_ERROR_IO); + } + + di_fini(root_node); + di_devlink_fini(&devlink_hdl); + + usbi_dbg(ctx, "%zu devices", (*discdevs)->len); + + return ((*discdevs)->len); +} + +static int +sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv) +{ + char filename[PATH_MAX + 1]; + + if (hpriv->eps[0].datafd > 0) { + return (LIBUSB_SUCCESS); + } + snprintf(filename, PATH_MAX, "%s/cntrl0", dpriv->ugenpath); + + 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)); + } + + snprintf(filename, PATH_MAX, "%s/cntrl0stat", dpriv->ugenpath); + hpriv->eps[0].statfd = open(filename, O_RDONLY); + if (hpriv->eps[0].statfd < 0) { + close(hpriv->eps[0].datafd); + hpriv->eps[0].datafd = -1; + + return(_errno_to_libusb(errno)); + } + + return (LIBUSB_SUCCESS); +} + +static void +sunos_usb_close_all_eps(sunos_dev_handle_priv_t *hdev) +{ + int i; + + /* not close ep0 */ + for (i = 1; i < USB_MAXENDPOINTS; i++) { + if (hdev->eps[i].datafd != -1) { + (void) close(hdev->eps[i].datafd); + hdev->eps[i].datafd = -1; + } + if (hdev->eps[i].statfd != -1) { + (void) close(hdev->eps[i].statfd); + hdev->eps[i].statfd = -1; + } + } +} + +static void +sunos_usb_close_ep0(sunos_dev_handle_priv_t *hdev) +{ + if (hdev->eps[0].datafd >= 0) { + close(hdev->eps[0].datafd); + close(hdev->eps[0].statfd); + hdev->eps[0].datafd = -1; + hdev->eps[0].statfd = -1; + } +} + +static uchar_t +sunos_usb_ep_index(uint8_t ep_addr) +{ + return ((ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK) + + ((ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? 16 : 0)); +} + +static int +sunos_find_interface(struct libusb_device_handle *hdev, + uint8_t endpoint, uint8_t *interface) +{ + struct libusb_config_descriptor *config; + int r; + int iface_idx; + + r = libusb_get_active_config_descriptor(hdev->dev, &config); + if (r < 0) { + return (LIBUSB_ERROR_INVALID_PARAM); + } + + for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { + const struct libusb_interface *iface = + &config->interface[iface_idx]; + int altsetting_idx; + + for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; + altsetting_idx++) { + const struct libusb_interface_descriptor *altsetting = + &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; + ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) { + *interface = iface_idx; + libusb_free_config_descriptor(config); + + return (LIBUSB_SUCCESS); + } + } + } + } + libusb_free_config_descriptor(config); + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +static int +sunos_check_device_and_status_open(struct libusb_device_handle *hdl, + uint8_t ep_addr, int ep_type) +{ + char filename[PATH_MAX + 1], statfilename[PATH_MAX + 1]; + char cfg_num[16], alt_num[16]; + int fd, fdstat, mode, e; + uint8_t ifc = 0; + uint8_t ep_index; + sunos_dev_handle_priv_t *hpriv; + + 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(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(HANDLE_CTX(hdl), "can't find interface for endpoint 0x%02x", + ep_addr); + + return (EACCES); + } + + /* create filename */ + if (hpriv->config_index > 0) { + (void) snprintf(cfg_num, sizeof(cfg_num), "cfg%d", + hpriv->config_index + 1); + } else { + bzero(cfg_num, sizeof(cfg_num)); + } + + if (hpriv->altsetting[ifc] > 0) { + (void) snprintf(alt_num, sizeof(alt_num), ".%d", + hpriv->altsetting[ifc]); + } else { + bzero(alt_num, sizeof(alt_num)); + } + + e = snprintf(filename, sizeof (filename), "%s/%sif%d%s%s%d", + hpriv->dpriv->ugenpath, cfg_num, ifc, alt_num, + (ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? "in" : "out", + ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK); + if (e < 0 || e >= (int)sizeof (filename)) { + usbi_dbg(HANDLE_CTX(hdl), + "path buffer overflow for endpoint 0x%02x", ep_addr); + return (EINVAL); + } + + e = snprintf(statfilename, sizeof (statfilename), "%sstat", filename); + if (e < 0 || e >= (int)sizeof (statfilename)) { + usbi_dbg(HANDLE_CTX(hdl), + "path buffer overflow for endpoint 0x%02x stat", ep_addr); + return (EINVAL); + } + + /* + * In case configuration has been switched, the xfer endpoint needs + * to be opened before the status endpoint, due to a ugen issue. + * However, to enable the one transfer mode for an Interrupt-In pipe, + * the status endpoint needs to be opened before the xfer endpoint. + * So, open the xfer mode first and close it immediately + * as a workaround. This will handle the configuration switch. + * Then, open the status endpoint. If for an Interrupt-in pipe, + * write the USB_EP_INTR_ONE_XFER control to the status endpoint + * to enable the one transfer mode. Then, re-open the xfer mode. + */ + if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + mode = O_RDWR; + } else if (ep_addr & LIBUSB_ENDPOINT_IN) { + mode = O_RDONLY; + } else { + mode = O_WRONLY; + } + /* Open the xfer endpoint first */ + if ((fd = open(filename, mode)) == -1) { + usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", filename, errno, + strerror(errno)); + + return (errno); + } + /* And immediately close the xfer endpoint */ + (void) close(fd); + + /* + * Open the status endpoint. + * If for an Interrupt-IN pipe, need to enable the one transfer mode + * by writing USB_EP_INTR_ONE_XFER control to the status endpoint + * before opening the xfer endpoint + */ + if ((ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) && + (ep_addr & LIBUSB_ENDPOINT_IN)) { + char control = USB_EP_INTR_ONE_XFER; + ssize_t count; + + /* Open the status endpoint with RDWR */ + if ((fdstat = open(statfilename, O_RDWR)) == -1) { + usbi_dbg(HANDLE_CTX(hdl), "can't open %s RDWR: errno %d (%s)", + statfilename, errno, strerror(errno)); + + return (errno); + } else { + count = write(fdstat, &control, sizeof(control)); + if (count != 1) { + /* this should have worked */ + usbi_dbg(HANDLE_CTX(hdl), "can't write to %s: errno %d (%s)", + statfilename, errno, strerror(errno)); + (void) close(fdstat); + + return (errno); + } + } + } else { + if ((fdstat = open(statfilename, O_RDONLY)) == -1) { + usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", statfilename, errno, + strerror(errno)); + + return (errno); + } + } + + /* Re-open the xfer endpoint */ + if ((fd = open(filename, mode)) == -1) { + usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", filename, errno, + strerror(errno)); + (void) close(fdstat); + + return (errno); + } + + hpriv->eps[ep_index].datafd = fd; + hpriv->eps[ep_index].statfd = fdstat; + usbi_dbg(HANDLE_CTX(hdl), "ep=0x%02x datafd=%d, statfd=%d", ep_addr, fd, fdstat); + + return (0); +} + +int +sunos_open(struct libusb_device_handle *handle) +{ + sunos_dev_handle_priv_t *hpriv; + sunos_dev_priv_t *dpriv; + int i; + int ret; + + hpriv = usbi_get_device_handle_priv(handle); + dpriv = usbi_get_device_priv(handle->dev); + hpriv->dpriv = dpriv; + + /* set all file descriptors to "closed" */ + for (i = 0; i < USB_MAXENDPOINTS; i++) { + hpriv->eps[i].datafd = -1; + hpriv->eps[i].statfd = -1; + } + + if (sunos_kernel_driver_active(handle, 0)) { + /* pretend we can open the device */ + return (LIBUSB_SUCCESS); + } + + if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) { + usbi_dbg(HANDLE_CTX(handle), "fail: %d", ret); + return (ret); + } + + return (LIBUSB_SUCCESS); +} + +void +sunos_close(struct libusb_device_handle *handle) +{ + sunos_dev_handle_priv_t *hpriv; + + usbi_dbg(HANDLE_CTX(handle), " "); + + hpriv = usbi_get_device_handle_priv(handle); + + sunos_usb_close_all_eps(hpriv); + sunos_usb_close_ep0(hpriv); +} + +int +sunos_get_active_config_descriptor(struct libusb_device *dev, + void *buf, size_t len) +{ + sunos_dev_priv_t *dpriv = usbi_get_device_priv(dev); + struct libusb_config_descriptor *cfg; + int proplen; + di_node_t node; + uint8_t *rdata; + + /* + * Keep raw configuration descriptors updated, in case config + * has ever been changed through setCfg. + */ + if ((node = di_init(dpriv->phypath, DINFOCPYALL)) == DI_NODE_NIL) { + 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(DEVICE_CTX(dev), "can't find raw config descriptors"); + + return (LIBUSB_ERROR_IO); + } + dpriv->raw_cfgdescr = realloc(dpriv->raw_cfgdescr, proplen); + if (dpriv->raw_cfgdescr == NULL) { + return (LIBUSB_ERROR_NO_MEM); + } else { + bcopy(rdata, dpriv->raw_cfgdescr, proplen); + dpriv->cfgvalue = ((struct libusb_config_descriptor *) + rdata)->bConfigurationValue; + } + di_fini(node); + + 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(DEVICE_CTX(dev), "path:%s len %zu", dpriv->phypath, len); + + return (len); +} + +int +sunos_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + void *buf, size_t len) +{ + UNUSED(idx); + /* XXX */ + return(sunos_get_active_config_descriptor(dev, buf, len)); +} + +int +sunos_get_configuration(struct libusb_device_handle *handle, uint8_t *config) +{ + sunos_dev_priv_t *dpriv = usbi_get_device_priv(handle->dev); + + *config = dpriv->cfgvalue; + + usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %u", *config); + + return (LIBUSB_SUCCESS); +} + +int +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(HANDLE_CTX(handle), "bConfigurationValue %d", config); + hpriv = usbi_get_device_handle_priv(handle); + + if (dpriv->ugenpath == NULL) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + if (config < 1) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + dpriv->cfgvalue = config; + hpriv->config_index = config - 1; + + return (LIBUSB_SUCCESS); +} + +int +sunos_claim_interface(struct libusb_device_handle *handle, uint8_t iface) +{ + UNUSED(handle); + + usbi_dbg(HANDLE_CTX(handle), "iface %u", iface); + + return (LIBUSB_SUCCESS); +} + +int +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(HANDLE_CTX(handle), "iface %u", iface); + + /* XXX: can we release it? */ + hpriv->altsetting[iface] = 0; + + return (LIBUSB_SUCCESS); +} + +int +sunos_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t iface, + uint8_t altsetting) +{ + 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(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting); + + if (dpriv->ugenpath == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + /* XXX: can we switch altsetting? */ + hpriv->altsetting[iface] = altsetting; + + return (LIBUSB_SUCCESS); +} + +static void +usb_dump_data(const void *data, size_t size) +{ + const uint8_t *p = data; + size_t i; + + if (getenv("LIBUSB_DEBUG") == NULL) { + return; + } + + (void) fprintf(stderr, "data dump:"); + for (i = 0; i < size; i++) { + if (i % 16 == 0) { + (void) fprintf(stderr, "\n%08zx ", i); + } + (void) fprintf(stderr, "%02x ", p[i]); + } + (void) fprintf(stderr, "\n"); +} + +static void +sunos_async_callback(union sigval arg) +{ + struct sunos_transfer_priv *tpriv = + (struct sunos_transfer_priv *)arg.sival_ptr; + struct libusb_transfer *xfer = tpriv->transfer; + struct aiocb *aiocb = &tpriv->aiocb; + int ret; + sunos_dev_handle_priv_t *hpriv; + uint8_t ep; + libusb_device_handle *dev_handle; + + dev_handle = xfer->dev_handle; + + /* libusb can forcibly interrupt transfer in do_close() */ + if (dev_handle != NULL) { + hpriv = usbi_get_device_handle_priv(dev_handle); + ep = sunos_usb_ep_index(xfer->endpoint); + + ret = aio_error(aiocb); + if (ret != 0) { + xfer->status = sunos_usb_get_status(TRANSFER_CTX(xfer), hpriv->eps[ep].statfd); + } else { + xfer->actual_length = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)->transferred = + aio_return(aiocb); + } + + usb_dump_data(xfer->buffer, xfer->actual_length); + + usbi_dbg(TRANSFER_CTX(xfer), "ret=%d, len=%d, actual_len=%d", ret, xfer->length, + xfer->actual_length); + + /* async notification */ + usbi_signal_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)); + } +} + +static int +sunos_do_async_io(struct libusb_transfer *transfer) +{ + int ret = -1; + struct aiocb *aiocb; + sunos_dev_handle_priv_t *hpriv; + uint8_t ep; + struct sunos_transfer_priv *tpriv; + + 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); + ep = sunos_usb_ep_index(transfer->endpoint); + + tpriv->transfer = transfer; + aiocb = &tpriv->aiocb; + bzero(aiocb, sizeof(*aiocb)); + aiocb->aio_fildes = hpriv->eps[ep].datafd; + aiocb->aio_buf = transfer->buffer; + aiocb->aio_nbytes = transfer->length; + aiocb->aio_lio_opcode = + ((transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) == + LIBUSB_ENDPOINT_IN) ? LIO_READ:LIO_WRITE; + aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD; + aiocb->aio_sigevent.sigev_value.sival_ptr = tpriv; + aiocb->aio_sigevent.sigev_notify_function = sunos_async_callback; + + if (aiocb->aio_lio_opcode == LIO_READ) { + ret = aio_read(aiocb); + } else { + ret = aio_write(aiocb); + } + + return (ret); +} + +/* return the number of bytes read/written */ +static ssize_t +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(ctx, "usb_do_io(): datafd=%d statfd=%d size=0x%zx flag=%s", + fd, stat_fd, size, flag? "WRITE":"READ"); + + switch (flag) { + case READ: + errno = 0; + ret = read(fd, data, size); + usb_dump_data(data, size); + break; + case WRITE: + usb_dump_data(data, size); + errno = 0; + ret = write(fd, data, size); + break; + } + + usbi_dbg(ctx, "usb_do_io(): amount=%zd", ret); + + if (ret < 0) { + int save_errno = errno; + + 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(ctx, stat_fd); + usbi_dbg(ctx, "io status=%d errno %d (%s)", error, + save_errno, strerror(save_errno)); + + if (status) { + *status = save_errno; + } + + return (save_errno); + + } else if (status) { + *status = 0; + } + + return (ret); +} + +static int +solaris_submit_ctrl_on_default(struct libusb_transfer *transfer) +{ + ssize_t ret = -1, setup_ret; + int status; + sunos_dev_handle_priv_t *hpriv; + struct libusb_device_handle *hdl = transfer->dev_handle; + uint16_t wLength; + uint8_t *data = transfer->buffer; + + hpriv = usbi_get_device_handle_priv(hdl); + wLength = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (hpriv->eps[0].datafd == -1) { + 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(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(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(TRANSFER_CTX(transfer), "error sending control msg: %zd", ret); + + return (LIBUSB_ERROR_IO); + } + + ret = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + /* Read the remaining bytes for IN request */ + if ((wLength) && ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) == + LIBUSB_ENDPOINT_IN)) { + 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); + } + + if (ret >= 0) { + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->transferred = ret; + } + usbi_dbg(TRANSFER_CTX(transfer), "Done: ctrl data bytes %zd", ret); + + /** + * Sync transfer handling. + * We should release transfer lock here and later get it back + * as usbi_handle_transfer_completion() takes its own transfer lock. + */ + usbi_mutex_unlock(&LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->lock); + ret = usbi_handle_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer), + transfer->status); + usbi_mutex_lock(&LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->lock); + + return (ret); +} + +int +sunos_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + int ret; + + 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(HANDLE_CTX(handle), "ret=%d", ret); + + return (ret); +} + +void +sunos_destroy_device(struct libusb_device *dev) +{ + sunos_dev_priv_t *dpriv = usbi_get_device_priv(dev); + + usbi_dbg(DEVICE_CTX(dev), "destroy everything"); + free(dpriv->raw_cfgdescr); + free(dpriv->ugenpath); + free(dpriv->phypath); +} + +int +sunos_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_device_handle *hdl; + int err = 0; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hdl = transfer->dev_handle; + + err = sunos_check_device_and_status_open(hdl, + transfer->endpoint, transfer->type); + if (err != 0) { + return (_errno_to_libusb(err)); + } + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + /* sync transfer */ + usbi_dbg(ITRANSFER_CTX(itransfer), "CTRL transfer: %d", transfer->length); + err = solaris_submit_ctrl_on_default(transfer); + break; + + case LIBUSB_TRANSFER_TYPE_BULK: + /* fallthru */ + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (transfer->type == LIBUSB_TRANSFER_TYPE_BULK) + usbi_dbg(ITRANSFER_CTX(itransfer), "BULK transfer: %d", transfer->length); + else + usbi_dbg(ITRANSFER_CTX(itransfer), "INTR transfer: %d", transfer->length); + err = sunos_do_async_io(transfer); + break; + + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + /* Isochronous/Stream is not supported */ + + /* fallthru */ + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + usbi_dbg(ITRANSFER_CTX(itransfer), "ISOC transfer: %d", transfer->length); + else + usbi_dbg(ITRANSFER_CTX(itransfer), "BULK STREAM transfer: %d", transfer->length); + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + return (err); +} + +int +sunos_cancel_transfer(struct usbi_transfer *itransfer) +{ + sunos_xfer_priv_t *tpriv; + sunos_dev_handle_priv_t *hpriv; + struct libusb_transfer *transfer; + struct aiocb *aiocb; + uint8_t ep; + int ret; + + tpriv = usbi_get_transfer_priv(itransfer); + aiocb = &tpriv->aiocb; + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hpriv = usbi_get_device_handle_priv(transfer->dev_handle); + ep = sunos_usb_ep_index(transfer->endpoint); + + ret = aio_cancel(hpriv->eps[ep].datafd, aiocb); + + 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)); + + if (ret != AIO_CANCELED) { + ret = _errno_to_libusb(errno); + } else { + /* + * we don't need to call usbi_handle_transfer_cancellation(), + * because we'll handle everything in sunos_async_callback. + */ + ret = LIBUSB_SUCCESS; + } + + return (ret); +} + +int +sunos_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +_errno_to_libusb(int err) +{ + usbi_dbg(NULL, "error: %s (%d)", strerror(err), err); + + switch (err) { + case EIO: + return (LIBUSB_ERROR_IO); + case EACCES: + return (LIBUSB_ERROR_ACCESS); + case ENOENT: + return (LIBUSB_ERROR_NO_DEVICE); + case ENOMEM: + return (LIBUSB_ERROR_NO_MEM); + case ETIMEDOUT: + return (LIBUSB_ERROR_TIMEOUT); + } + + return (LIBUSB_ERROR_OTHER); +} + +/* + * sunos_usb_get_status: + * gets status of endpoint + * + * Returns: ugen's last cmd status + */ +static int +sunos_usb_get_status(struct libusb_context *ctx, int fd) +{ + int status; + ssize_t ret; + + 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(ctx, "No Error"); + break; + case USB_LC_STAT_CRC: + usbi_dbg(ctx, "CRC Timeout Detected\n"); + break; + case USB_LC_STAT_BITSTUFFING: + usbi_dbg(ctx, "Bit Stuffing Violation\n"); + break; + case USB_LC_STAT_DATA_TOGGLE_MM: + usbi_dbg(ctx, "Data Toggle Mismatch\n"); + break; + case USB_LC_STAT_STALL: + usbi_dbg(ctx, "End Point Stalled\n"); + break; + case USB_LC_STAT_DEV_NOT_RESP: + usbi_dbg(ctx, "Device is Not Responding\n"); + break; + case USB_LC_STAT_PID_CHECKFAILURE: + usbi_dbg(ctx, "PID Check Failure\n"); + break; + case USB_LC_STAT_UNEXP_PID: + usbi_dbg(ctx, "Unexpected PID\n"); + break; + case USB_LC_STAT_DATA_OVERRUN: + usbi_dbg(ctx, "Data Exceeded Size\n"); + break; + case USB_LC_STAT_DATA_UNDERRUN: + usbi_dbg(ctx, "Less data received\n"); + break; + case USB_LC_STAT_BUFFER_OVERRUN: + usbi_dbg(ctx, "Buffer Size Exceeded\n"); + break; + case USB_LC_STAT_BUFFER_UNDERRUN: + usbi_dbg(ctx, "Buffer Underrun\n"); + break; + case USB_LC_STAT_TIMEOUT: + usbi_dbg(ctx, "Command Timed Out\n"); + break; + case USB_LC_STAT_NOT_ACCESSED: + usbi_dbg(ctx, "Not Accessed by h/w\n"); + break; + case USB_LC_STAT_UNSPECIFIED_ERR: + usbi_dbg(ctx, "Unspecified Error\n"); + break; + case USB_LC_STAT_NO_BANDWIDTH: + usbi_dbg(ctx, "No Bandwidth\n"); + break; + case USB_LC_STAT_HW_ERR: + usbi_dbg(ctx, "Host Controller h/w Error\n"); + break; + case USB_LC_STAT_SUSPENDED: + usbi_dbg(ctx, "Device was Suspended\n"); + break; + case USB_LC_STAT_DISCONNECTED: + usbi_dbg(ctx, "Device was Disconnected\n"); + break; + case USB_LC_STAT_INTR_BUF_FULL: + usbi_dbg(ctx, "Interrupt buffer was full\n"); + break; + case USB_LC_STAT_INVALID_REQ: + usbi_dbg(ctx, "Request was Invalid\n"); + break; + case USB_LC_STAT_INTERRUPTED: + usbi_dbg(ctx, "Request was Interrupted\n"); + break; + case USB_LC_STAT_NO_RESOURCES: + usbi_dbg(ctx, "No resources available for " + "request\n"); + break; + case USB_LC_STAT_INTR_POLLING_FAILED: + usbi_dbg(ctx, "Failed to Restart Poll"); + break; + default: + usbi_dbg(ctx, "Error Not Determined %d\n", + status); + break; + } + } else { + usbi_dbg(ctx, "read stat error: %s",strerror(errno)); + status = -1; + } + + return (status); +} + +const struct usbi_os_backend usbi_backend = { + .name = "Solaris", + .caps = 0, + .get_device_list = sunos_get_device_list, + .get_active_config_descriptor = sunos_get_active_config_descriptor, + .get_config_descriptor = sunos_get_config_descriptor, + .open = sunos_open, + .close = sunos_close, + .get_configuration = sunos_get_configuration, + .set_configuration = sunos_set_configuration, + .claim_interface = sunos_claim_interface, + .release_interface = sunos_release_interface, + .set_interface_altsetting = sunos_set_interface_altsetting, + .clear_halt = sunos_clear_halt, + .kernel_driver_active = sunos_kernel_driver_active, + .detach_kernel_driver = sunos_detach_kernel_driver, + .attach_kernel_driver = sunos_attach_kernel_driver, + .destroy_device = sunos_destroy_device, + .submit_transfer = sunos_submit_transfer, + .cancel_transfer = sunos_cancel_transfer, + .handle_transfer_completion = sunos_handle_transfer_completion, + .device_priv_size = sizeof(sunos_dev_priv_t), + .device_handle_priv_size = sizeof(sunos_dev_handle_priv_t), + .transfer_priv_size = sizeof(sunos_xfer_priv_t), +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.h new file mode 100644 index 00000000..2988398d --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/sunos_usb.h @@ -0,0 +1,79 @@ +/* + * + * Copyright (c) 2016, Oracle and/or its affiliates. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_SUNOS_H +#define LIBUSB_SUNOS_H + +#include +#include +#include "libusbi.h" + +#define READ 0 +#define WRITE 1 + +typedef struct sunos_device_priv { + uint8_t cfgvalue; /* active config value */ + uint8_t *raw_cfgdescr; /* active config descriptor */ + char *ugenpath; /* name of the ugen(4) node */ + char *phypath; /* physical path */ +} sunos_dev_priv_t; + +typedef struct endpoint { + int datafd; /* data file */ + int statfd; /* state file */ +} sunos_ep_priv_t; + +typedef struct sunos_device_handle_priv { + uint8_t altsetting[USB_MAXINTERFACES]; /* a interface's alt */ + uint8_t config_index; + sunos_ep_priv_t eps[USB_MAXENDPOINTS]; + sunos_dev_priv_t *dpriv; /* device private */ +} sunos_dev_handle_priv_t; + +typedef struct sunos_transfer_priv { + struct aiocb aiocb; + struct libusb_transfer *transfer; +} sunos_xfer_priv_t; + +struct node_args { + struct libusb_context *ctx; + struct discovered_devs **discdevs; + const char *last_ugenpath; + di_devlink_handle_t dlink_hdl; +}; + +struct devlink_cbarg { + struct node_args *nargs; /* di node walk arguments */ + di_node_t myself; /* the di node */ + di_minor_t minor; +}; + +typedef struct walk_link { + char *path; + int len; + char **linkpp; +} walk_link_t; + +/* AIO callback args */ +struct aio_callback_args{ + struct libusb_transfer *transfer; + struct aiocb aiocb; +}; + +#endif /* LIBUSB_SUNOS_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.c new file mode 100644 index 00000000..786a3349 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.c @@ -0,0 +1,126 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright © 2011 Vitali Lovich + * Copyright © 2011 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include +#include +#if defined(__ANDROID__) +# include +#elif defined(__HAIKU__) +# include +#elif defined(__linux__) +# include +# include +#elif defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) +# include +#elif defined(__sun__) +# include +#endif + +void usbi_cond_init(usbi_cond_t *cond) +{ +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + pthread_condattr_t condattr; + + PTHREAD_CHECK(pthread_condattr_init(&condattr)); + PTHREAD_CHECK(pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC)); + PTHREAD_CHECK(pthread_cond_init(cond, &condattr)); + PTHREAD_CHECK(pthread_condattr_destroy(&condattr)); +#else + PTHREAD_CHECK(pthread_cond_init(cond, NULL)); +#endif +} + +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv) +{ + struct timespec timeout; + int r; + +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + usbi_get_monotonic_time(&timeout); +#else + usbi_get_real_time(&timeout); +#endif + + timeout.tv_sec += tv->tv_sec; + timeout.tv_nsec += tv->tv_usec * 1000L; + if (timeout.tv_nsec >= NSEC_PER_SEC) { + timeout.tv_nsec -= NSEC_PER_SEC; + timeout.tv_sec++; + } + + r = pthread_cond_timedwait(cond, mutex, &timeout); + if (r == 0) + return 0; + else if (r == ETIMEDOUT) + return LIBUSB_ERROR_TIMEOUT; + else + return LIBUSB_ERROR_OTHER; +} + +unsigned long usbi_get_tid(void) +{ + static _Thread_local unsigned long tl_tid; + unsigned long tid; + + if (tl_tid) + return tl_tid; + +#if defined(__ANDROID__) + tid = (unsigned long)gettid(); +#elif defined(__APPLE__) +#ifdef HAVE_PTHREAD_THREADID_NP + uint64_t thread_id; + + if (pthread_threadid_np(NULL, &thread_id) == 0) + tid = (unsigned long)thread_id; + else + tid = ULONG_MAX; +#else + tid = (unsigned long)pthread_mach_thread_np(pthread_self()); +#endif +#elif defined(__HAIKU__) + tid = (unsigned long)get_pthread_thread_id(pthread_self()); +#elif defined(__linux__) + tid = (unsigned long)syscall(SYS_gettid); +#elif defined(__NetBSD__) + tid = (unsigned long)_lwp_self(); +#elif defined(__OpenBSD__) + tid = (unsigned long)getthrid(); +#elif defined(__sun__) + tid = (unsigned long)_lwp_self(); +#else + tid = ULONG_MAX; +#endif + + if (tid == ULONG_MAX) { + /* If we don't have a thread ID, at least return a unique + * value that can be used to distinguish individual + * threads. */ + tid = (unsigned long)(uintptr_t)pthread_self(); + } + + return tl_tid = tid; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.h new file mode 100644 index 00000000..eb7e2468 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_posix.h @@ -0,0 +1,104 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright © 2010 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_POSIX_H +#define LIBUSB_THREADS_POSIX_H + +#include + +#define PTHREAD_CHECK(expression) ASSERT_EQ(expression, 0) + +#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t usbi_mutex_static_t; +static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_lock(mutex)); +} +static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_unlock(mutex)); +} + +typedef pthread_mutex_t usbi_mutex_t; +static inline void usbi_mutex_init(usbi_mutex_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_init(mutex, NULL)); +} +static inline void usbi_mutex_lock(usbi_mutex_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_lock(mutex)); +} +static inline void usbi_mutex_unlock(usbi_mutex_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_unlock(mutex)); +} +static inline int usbi_mutex_trylock(usbi_mutex_t *mutex) +{ + int mutexIsLocked = pthread_mutex_trylock(mutex) == 0; + return mutexIsLocked; +} +static inline void usbi_mutex_destroy(usbi_mutex_t *mutex) +{ + PTHREAD_CHECK(pthread_mutex_destroy(mutex)); +} + +#define USBI_COND_INITIALIZER PTHREAD_COND_INITIALIZER +typedef pthread_cond_t usbi_cond_t; +void usbi_cond_init(usbi_cond_t *cond); +static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) +{ + PTHREAD_CHECK(pthread_cond_wait(cond, mutex)); +} +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv); +static inline void usbi_cond_signal(usbi_cond_t *cond) +{ + PTHREAD_CHECK(pthread_cond_signal(cond)); +} +static inline void usbi_cond_broadcast(usbi_cond_t *cond) +{ + PTHREAD_CHECK(pthread_cond_broadcast(cond)); +} +static inline void usbi_cond_destroy(usbi_cond_t *cond) +{ + PTHREAD_CHECK(pthread_cond_destroy(cond)); +} + +typedef pthread_key_t usbi_tls_key_t; +static inline void usbi_tls_key_create(usbi_tls_key_t *key) +{ + PTHREAD_CHECK(pthread_key_create(key, NULL)); +} +static inline void *usbi_tls_key_get(usbi_tls_key_t key) +{ + return pthread_getspecific(key); +} +static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr) +{ + PTHREAD_CHECK(pthread_setspecific(key, ptr)); +} +static inline void usbi_tls_key_delete(usbi_tls_key_t key) +{ + PTHREAD_CHECK(pthread_key_delete(key)); +} + +unsigned long usbi_get_tid(void); + +#endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.c new file mode 100644 index 00000000..4a57f42e --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.c @@ -0,0 +1,40 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright © 2010 Michael Plante + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv) +{ + DWORD millis; + + millis = (DWORD)(tv->tv_sec * 1000L) + (tv->tv_usec / 1000L); + /* round up to next millisecond */ + if (tv->tv_usec % 1000L) + millis++; + + if (SleepConditionVariableCS(cond, mutex, millis)) + return 0; + else if (GetLastError() == ERROR_TIMEOUT) + return LIBUSB_ERROR_TIMEOUT; + else + return LIBUSB_ERROR_OTHER; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.h new file mode 100644 index 00000000..c67c6918 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/threads_windows.h @@ -0,0 +1,114 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright © 2010 Michael Plante + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_WINDOWS_H +#define LIBUSB_THREADS_WINDOWS_H + +#define WINAPI_CHECK(expression) ASSERT_NE(expression, 0) + +#define USBI_MUTEX_INITIALIZER 0L +typedef LONG usbi_mutex_static_t; +static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex) +{ + while (InterlockedExchange(mutex, 1L) == 1L) + SleepEx(0, TRUE); +} +static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) +{ + InterlockedExchange(mutex, 0L); +} + +typedef CRITICAL_SECTION usbi_mutex_t; +static inline void usbi_mutex_init(usbi_mutex_t *mutex) +{ + InitializeCriticalSection(mutex); +} +static inline void usbi_mutex_lock(usbi_mutex_t *mutex) +{ + EnterCriticalSection(mutex); +} +static inline void usbi_mutex_unlock(usbi_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); +} +static inline int usbi_mutex_trylock(usbi_mutex_t *mutex) +{ + int mutexIsLocked = TryEnterCriticalSection(mutex) != 0; + return mutexIsLocked; +} +static inline void usbi_mutex_destroy(usbi_mutex_t *mutex) +{ + DeleteCriticalSection(mutex); +} + +#if !defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED) +#define HAVE_STRUCT_TIMESPEC 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC || _TIMESPEC_DEFINED */ + +typedef CONDITION_VARIABLE usbi_cond_t; +static inline void usbi_cond_init(usbi_cond_t *cond) +{ + InitializeConditionVariable(cond); +} +static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) +{ + WINAPI_CHECK(SleepConditionVariableCS(cond, mutex, INFINITE)); +} +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv); +static inline void usbi_cond_broadcast(usbi_cond_t *cond) +{ + WakeAllConditionVariable(cond); +} +static inline void usbi_cond_destroy(usbi_cond_t *cond) +{ + UNUSED(cond); +} + +typedef DWORD usbi_tls_key_t; +static inline void usbi_tls_key_create(usbi_tls_key_t *key) +{ + *key = TlsAlloc(); + assert(*key != TLS_OUT_OF_INDEXES); +} +static inline void *usbi_tls_key_get(usbi_tls_key_t key) +{ + return TlsGetValue(key); +} +static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr) +{ + WINAPI_CHECK(TlsSetValue(key, ptr)); +} +static inline void usbi_tls_key_delete(usbi_tls_key_t key) +{ + WINAPI_CHECK(TlsFree(key)); +} + +static inline unsigned long usbi_get_tid(void) +{ + return (unsigned long)GetCurrentThreadId(); +} + +#endif /* LIBUSB_THREADS_WINDOWS_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.c new file mode 100644 index 00000000..abad32a1 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.c @@ -0,0 +1,1008 @@ +/* + * windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * Hash table functions adapted from glibc, by Ulrich Drepper et al. + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include + +#include "libusbi.h" +#include "windows_common.h" + +#if defined(LIBUSB_WINDOWS_HOTPLUG) +#include "windows_hotplug.h" +#endif + +#define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime + +#define STATUS_SUCCESS ((ULONG_PTR)0UL) + +// Public +enum windows_version windows_version = WINDOWS_UNDEFINED; + +// Global variables for init/exit +static unsigned int init_count; +static bool usbdk_available; + +/* +* Converts a windows error to human readable string +* uses retval as errorcode, or, if 0, use GetLastError() +*/ +#if defined(ENABLE_LOGGING) +const char *windows_error_str(DWORD error_code) +{ + static char err_string[256]; + + DWORD size; + int len; + + if (error_code == 0) + error_code = GetLastError(); + + len = sprintf(err_string, "[%lu] ", ULONG_CAST(error_code)); + + // Translate codes returned by SetupAPI. The ones we are dealing with are either + // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx + switch (error_code & 0xE0000000) { + case 0: + error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified + break; + case 0xE0000000: + error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); + break; + default: + break; + } + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + &err_string[len], sizeof(err_string) - len, NULL); + if (size == 0) { + DWORD format_error = GetLastError(); + if (format_error) + snprintf(err_string, sizeof(err_string), + "Windows error code %lu (FormatMessage error code %lu)", + ULONG_CAST(error_code), ULONG_CAST(format_error)); + else + snprintf(err_string, sizeof(err_string), "Unknown error code %lu", + ULONG_CAST(error_code)); + } else { + // Remove CRLF from end of message, if present + size_t pos = len + size - 2; + if (err_string[pos] == '\r') + err_string[pos] = '\0'; + } + + return err_string; +} +#endif + +/* + * Dynamically loads a DLL from the Windows system directory. Unlike the + * LoadLibraryA() function, this function will not search through any + * directories to try and find the library. + */ +HMODULE load_system_library(struct libusb_context *ctx, const char *name) +{ + char library_path[MAX_PATH]; + char *filename_start; + UINT length; + + length = GetSystemDirectoryA(library_path, sizeof(library_path)); + if ((length == 0) || (length >= (UINT)sizeof(library_path))) { + usbi_err(ctx, "program assertion failed - could not get system directory"); + return NULL; + } + + filename_start = library_path + length; + // Append '\' + name + ".dll" + NUL + length += 1 + (UINT)strlen(name) + 4 + 1; + if (length >= (UINT)sizeof(library_path)) { + usbi_err(ctx, "program assertion failed - library path buffer overflow"); + return NULL; + } + + sprintf(filename_start, "\\%s.dll", name); + return LoadLibraryA(library_path); +} + +/* Hash table functions - modified From glibc 2.3.2: + [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + [Knuth] The Art of Computer Programming, part 3 (6.4) */ + +#define HTAB_SIZE 1021UL // *MUST* be a prime number!! + +typedef struct htab_entry { + unsigned long used; + char *str; +} htab_entry; + +static htab_entry *htab_table; +static usbi_mutex_t htab_mutex; +static unsigned long htab_filled; + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +static bool htab_create(struct libusb_context *ctx) +{ + if (htab_table != NULL) { + usbi_err(ctx, "program assertion failed - hash table already allocated"); + return true; + } + + // Create a mutex + usbi_mutex_init(&htab_mutex); + + usbi_dbg(ctx, "using %lu entries hash table", HTAB_SIZE); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = calloc(HTAB_SIZE + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return false; + } + + return true; +} + +/* After using the hash table it has to be destroyed. */ +static void htab_destroy(void) +{ + unsigned long i; + + if (htab_table == NULL) + return; + + for (i = 0; i < HTAB_SIZE; i++) + free(htab_table[i].str); + + safe_free(htab_table); + + usbi_mutex_destroy(&htab_mutex); +} + +/* This is the search function. It uses double hashing with open addressing. + We use a trick to speed up the lookup. The table is created with one + more element available. This enables us to use the index zero special. + This index will never be used because we store the first hash index in + the field used where zero means not used. Every other value means used. + The used field can be used as a first fast comparison for equality of + the stored and the parameter value. This helps to prevent unnecessary + expensive calls of strcmp. */ +unsigned long htab_hash(const char *str) +{ + unsigned long hval, hval2; + unsigned long idx; + unsigned long r = 5381UL; + int c; + const char *sz = str; + + if (str == NULL) + return 0; + + // Compute main hash value (algorithm suggested by Nokia) + while ((c = *sz++) != 0) + r = ((r << 5) + r) + c; + if (r == 0) + ++r; + + // compute table hash: simply take the modulus + hval = r % HTAB_SIZE; + if (hval == 0) + ++hval; + + // Try the first index + idx = hval; + + // Mutually exclusive access (R/W lock would be better) + usbi_mutex_lock(&htab_mutex); + + if (htab_table[idx].used) { + if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) + goto out_unlock; // existing hash + + 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); + + do { + // Because size is prime this guarantees to step through all available indexes + if (idx <= hval2) + idx = HTAB_SIZE + idx - hval2; + else + idx -= hval2; + + // If we visited all entries leave the loop unsuccessfully + if (idx == hval) + break; + + // If entry is found use it. + if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) + goto out_unlock; + } while (htab_table[idx].used); + } + + // Not found => New entry + + // If the table is full return an error + if (htab_filled >= HTAB_SIZE) { + usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE); + idx = 0UL; + goto out_unlock; + } + + htab_table[idx].str = _strdup(str); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + idx = 0UL; + goto out_unlock; + } + + htab_table[idx].used = hval; + ++htab_filled; + +out_unlock: + usbi_mutex_unlock(&htab_mutex); + + return idx; +} + +enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS status) +{ + if (USBD_SUCCESS(status)) + return LIBUSB_TRANSFER_COMPLETED; + + switch (status) { + case USBD_STATUS_TIMEOUT: + return LIBUSB_TRANSFER_TIMED_OUT; + case USBD_STATUS_CANCELED: + return LIBUSB_TRANSFER_CANCELLED; + case USBD_STATUS_ENDPOINT_HALTED: + case USBD_STATUS_STALL_PID: + return LIBUSB_TRANSFER_STALL; + case USBD_STATUS_DEVICE_GONE: + return LIBUSB_TRANSFER_NO_DEVICE; + default: + usbi_dbg(NULL, "USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status)); + return LIBUSB_TRANSFER_ERROR; + } +} + +/* + * Make a transfer complete synchronously + */ +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_CTX(transfer), "transfer %p, length %lu", transfer, ULONG_CAST(size)); + + overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS; + overlapped->InternalHigh = (ULONG_PTR)size; + + 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 */ +static BOOL is_x64(void) +{ + BOOL ret = FALSE; + + // Detect if we're running a 32 or 64 bit system + if (sizeof(uintptr_t) < 8) { + IsWow64Process(GetCurrentProcess(), &ret); + } else { + ret = TRUE; + } + + return ret; +} + +static enum windows_version get_windows_version(void) +{ + enum windows_version winver; + OSVERSIONINFOEXA vi, vi2; + unsigned major, minor, version; + ULONGLONG major_equal, minor_equal; + const char *w, *arch; + bool ws; + +#ifndef ENABLE_LOGGING + UNUSED(w); UNUSED(arch); +#endif + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) + return WINDOWS_UNDEFINED; + } + + if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) + return WINDOWS_UNDEFINED; + + 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++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMajorVersion = major; + if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal)) + continue; + + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; + vi.dwMinorVersion = 0; + } + + minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal)) + continue; + + vi.dwMinorVersion = minor; + break; + } + + break; + } + } + + if ((vi.dwMajorVersion > 0xf) || (vi.dwMinorVersion > 0xf)) + return WINDOWS_UNDEFINED; + + 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; + case 0x52: winver = WINDOWS_2003; w = "2003"; break; + case 0x60: winver = WINDOWS_VISTA; w = (ws ? "Vista" : "2008"); break; + case 0x61: winver = WINDOWS_7; w = (ws ? "7" : "2008_R2"); break; + 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"); + 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_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(NULL, "Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch); + else if (vi.wServicePackMajor) + usbi_dbg(NULL, "Windows %s SP%u %s", w, vi.wServicePackMajor, arch); + else + usbi_dbg(NULL, "Windows %s %s", w, arch); + + return winver; +} + +static unsigned __stdcall windows_iocp_thread(void *arg) +{ + struct libusb_context *ctx = arg; + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + HANDLE iocp = priv->completion_port; + 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(ctx, "I/O completion thread started"); + + while (true) { + overlapped = NULL; + if (!GetQueuedCompletionStatus(iocp, &num_bytes, &completion_key, &overlapped, INFINITE) && (overlapped == NULL)) { + usbi_err(ctx, "GetQueuedCompletionStatus failed: %s", windows_error_str(0)); + break; + } + + if (overlapped == NULL) { + // Signal to quit + if (completion_key != (ULONG_PTR)ctx) + usbi_err(ctx, "program assertion failed - overlapped is NULL"); + break; + } + + // 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", + overlapped, dev_handle); + continue; + } + + itransfer = TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv); +#ifdef ENABLE_LOGGING + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "transfer %p completed, length %lu", + transfer, ULONG_CAST(num_bytes)); +#endif + usbi_signal_transfer_completion(itransfer); + } + + usbi_dbg(ctx, "I/O completion thread exiting"); + + return 0; +} + +static int windows_init(struct libusb_context *ctx) +{ + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + bool winusb_backend_init = false; + int r; + + // 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? + windows_version = get_windows_version(); + if (windows_version == WINDOWS_UNDEFINED) { + usbi_err(ctx, "failed to detect Windows version"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } else if (windows_version < WINDOWS_VISTA) { + usbi_err(ctx, "Windows version is too old"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + if (!htab_create(ctx)) { + r = LIBUSB_ERROR_NO_MEM; + goto init_exit; + } + + r = winusb_backend.init(ctx); + if (r != LIBUSB_SUCCESS) + goto init_exit; + winusb_backend_init = true; + + r = usbdk_backend.init(ctx); + if (r == LIBUSB_SUCCESS) { + usbi_dbg(ctx, "UsbDk backend is available"); + usbdk_available = true; + } else { + usbi_info(ctx, "UsbDk backend is not available"); + // Do not report this as an error + } + } + + // By default, new contexts will use the WinUSB backend + priv->backend = &winusb_backend; + + r = LIBUSB_ERROR_NO_MEM; + + // Use an I/O completion port to manage all transfers for this context + priv->completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (priv->completion_port == NULL) { + usbi_err(ctx, "failed to create I/O completion port: %s", windows_error_str(0)); + goto init_exit; + } + + // And a dedicated thread to wait for I/O completions + priv->completion_port_thread = (HANDLE)_beginthreadex(NULL, 0, windows_iocp_thread, ctx, 0, NULL); + if (priv->completion_port_thread == NULL) { + usbi_err(ctx, "failed to create I/O completion port thread"); + CloseHandle(priv->completion_port); + goto init_exit; + } + + r = LIBUSB_SUCCESS; + +#if defined(LIBUSB_WINDOWS_HOTPLUG) + if (init_count == 1) { + r = windows_start_event_monitor(); // Start-up hotplug event handler + if (r != LIBUSB_SUCCESS) { + usbi_err(ctx, "error starting hotplug event monitor"); + goto init_exit; + } + } + + windows_initial_scan_devices(ctx); +#endif + +init_exit: // Holds semaphore here + if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed? + if (usbdk_available) { + usbdk_backend.exit(ctx); + usbdk_available = false; + } + if (winusb_backend_init) + winusb_backend.exit(ctx); + htab_destroy(); + --init_count; + } + + return r; +} + +static void windows_exit(struct libusb_context *ctx) +{ + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + + // 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)) + usbi_err(ctx, "failed to post I/O completion: %s", windows_error_str(0)); + + if (WaitForSingleObject(priv->completion_port_thread, INFINITE) == WAIT_FAILED) + usbi_err(ctx, "failed to wait for I/O completion port thread: %s", windows_error_str(0)); + + CloseHandle(priv->completion_port_thread); + CloseHandle(priv->completion_port); + + // Only works if exits and inits are balanced exactly + if (--init_count == 0) { // Last exit +#if defined(LIBUSB_WINDOWS_HOTPLUG) + windows_stop_event_monitor(); +#endif + if (usbdk_available) { + usbdk_backend.exit(ctx); + usbdk_available = false; + } + winusb_backend.exit(ctx); + htab_destroy(); + } +} + +static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap) +{ + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + + UNUSED(ap); + + if (option == LIBUSB_OPTION_USE_USBDK) { + if (!usbdk_available) { + usbi_err(ctx, "UsbDk backend not available"); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_dbg(ctx, "switching context %p to use UsbDk backend", ctx); + priv->backend = &usbdk_backend; + return LIBUSB_SUCCESS; + } + + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +#if !defined(LIBUSB_WINDOWS_HOTPLUG) +static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **discdevs) +{ + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + return priv->backend->get_device_list(ctx, discdevs); +} +#endif + +static int windows_get_device_string(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length) +{ + struct windows_context_priv* priv = usbi_get_context_priv(DEVICE_CTX(dev)); + if (NULL != priv->backend->get_device_string) { + return priv->backend->get_device_string(dev, string_type, data, length); + } + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +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); +} + +static void windows_close(struct libusb_device_handle *dev_handle) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + priv->backend->close(dev_handle); +} + +static int windows_get_active_config_descriptor(struct libusb_device *dev, + void *buffer, size_t len) +{ + struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev)); + return priv->backend->get_active_config_descriptor(dev, buffer, len); +} + +static int windows_get_config_descriptor(struct libusb_device *dev, + uint8_t config_index, void *buffer, size_t len) +{ + struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev)); + return priv->backend->get_config_descriptor(dev, config_index, buffer, len); +} + +static int windows_get_config_descriptor_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, void **buffer) +{ + struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev)); + return priv->backend->get_config_descriptor_by_value(dev, bConfigurationValue, buffer); +} + +static int windows_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->get_configuration(dev_handle, config); +} + +static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + if (config == -1) + config = 0; + return priv->backend->set_configuration(dev_handle, (uint8_t)config); +} + +static int windows_claim_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->claim_interface(dev_handle, interface_number); +} + +static int windows_release_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->release_interface(dev_handle, interface_number); +} + +static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, + uint8_t interface_number, uint8_t altsetting) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->set_interface_altsetting(dev_handle, interface_number, altsetting); +} + +static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->clear_halt(dev_handle, endpoint); +} + +static int windows_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + return priv->backend->reset_device(dev_handle); +} + +static void windows_destroy_device(struct libusb_device *dev) +{ + struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev)); + priv->backend->destroy_device(dev); +} + +static int windows_endpoint_supports_raw_io(libusb_device_handle* dev_handle, + uint8_t endpoint) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + if (priv->backend->endpoint_supports_raw_io) + return priv->backend->endpoint_supports_raw_io(dev_handle, endpoint); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_endpoint_set_raw_io(libusb_device_handle* dev_handle, + uint8_t endpoint, int enable) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + + if (priv->backend->endpoint_supports_raw_io == NULL + || priv->backend->endpoint_supports_raw_io(dev_handle, endpoint) != 1 + || priv->backend->endpoint_set_raw_io == NULL) + return LIBUSB_ERROR_NOT_SUPPORTED; + + return priv->backend->endpoint_set_raw_io(dev_handle, endpoint, enable); +} + +static int windows_get_max_raw_io_transfer_size(struct libusb_device_handle *dev_handle, + uint8_t endpoint) +{ + struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + if (priv->backend->get_max_raw_io_transfer_size) + return priv->backend->get_max_raw_io_transfer_size(dev_handle, endpoint); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_submit_transfer(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 = 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; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + usbi_warn(ctx, "bulk stream transfers are not yet supported on this platform"); + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(ctx, "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (transfer_priv->handle != NULL) { + usbi_err(ctx, "program assertion failed - transfer HANDLE is not NULL"); + 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; + return r; + } + + // The backend should set the HANDLE used for each submitted transfer + // by calling set_transfer_priv_handle() + if (transfer_priv->handle == NULL) + usbi_err(ctx, "program assertion failed - transfer HANDLE is NULL after transfer was submitted"); + + return r; +} + +static int windows_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct windows_context_priv *priv = usbi_get_context_priv(ITRANSFER_CTX(itransfer)); + struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); + + // Try CancelIoEx() on the transfer + // If that fails, fall back to the backend's cancel_transfer() + // function if it is available + if (CancelIoEx(transfer_priv->handle, &transfer_priv->overlapped)) + return LIBUSB_SUCCESS; + else if (GetLastError() == ERROR_NOT_FOUND) + return LIBUSB_ERROR_NOT_FOUND; + + if (priv->backend->cancel_transfer) + return priv->backend->cancel_transfer(itransfer); + + usbi_warn(ITRANSFER_CTX(itransfer), "cancellation not supported for this transfer's driver"); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + const struct windows_backend *backend = priv->backend; + struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); + enum libusb_transfer_status status, istatus; + DWORD result, bytes_transferred; + HANDLE transfer_handle; + + /* + * The submit path runs with itransfer->lock held. Grab the handle under + * the same lock so we do not race with submission/completion bookkeeping. + */ + usbi_mutex_lock(&itransfer->lock); + transfer_handle = transfer_priv->handle; + usbi_mutex_unlock(&itransfer->lock); + + if (GetOverlappedResult(transfer_handle, &transfer_priv->overlapped, &bytes_transferred, FALSE)) + result = NO_ERROR; + else + result = GetLastError(); + +#ifdef ENABLE_LOGGING + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu", + transfer, ULONG_CAST(result), ULONG_CAST(bytes_transferred)); +#endif + + switch (result) { + case NO_ERROR: + status = backend->copy_transfer_data(itransfer, bytes_transferred); + break; + case ERROR_GEN_FAILURE: + usbi_dbg(ctx, "detected endpoint stall"); + status = LIBUSB_TRANSFER_STALL; + break; + case ERROR_SEM_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(ctx, "failed to copy partial data in aborted operation: %d", (int)istatus); + + 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(ctx, "detected device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + default: + usbi_err(ctx, "detected I/O error %lu: %s", + ULONG_CAST(result), windows_error_str(result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + usbi_mutex_lock(&itransfer->lock); + transfer_priv->handle = NULL; + usbi_mutex_unlock(&itransfer->lock); + + // Backend-specific cleanup + backend->clear_transfer_priv(itransfer); + + if (status == LIBUSB_TRANSFER_CANCELLED) + return usbi_handle_transfer_cancellation(itransfer); + else + return usbi_handle_transfer_completion(itransfer, status); +} + +#ifndef HAVE_CLOCK_GETTIME +void usbi_get_monotonic_time(struct timespec *tp) +{ + static LONG hires_counter_init; + static uint64_t hires_ticks_to_ps; + static uint64_t hires_frequency; + LARGE_INTEGER hires_counter; + + if (InterlockedExchange(&hires_counter_init, 1L) == 0L) { + LARGE_INTEGER li_frequency; + + // Microsoft says that the QueryPerformanceFrequency() and + // QueryPerformanceCounter() functions always succeed on XP and later + QueryPerformanceFrequency(&li_frequency); + + // The hires frequency can go as high as 4 GHz, so we'll use a conversion + // to picoseconds to compute the tv_nsecs part + hires_frequency = li_frequency.QuadPart; + hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; + } + + QueryPerformanceCounter(&hires_counter); + tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); + tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) * hires_ticks_to_ps) / UINT64_C(1000)); +} +#endif + +// NB: MSVC6 does not support named initializers. +const struct usbi_os_backend usbi_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + windows_init, + windows_exit, + windows_set_option, +#if defined(LIBUSB_WINDOWS_HOTPLUG) + NULL, /* get_device_list */ +#else + windows_get_device_list, +#endif + windows_get_device_string, + NULL, /* hotplug_poll */ + NULL, /* wrap_sys_device */ + windows_open, + windows_close, + windows_get_active_config_descriptor, + windows_get_config_descriptor, + windows_get_config_descriptor_by_value, + windows_get_configuration, + windows_set_configuration, + windows_claim_interface, + windows_release_interface, + windows_set_interface_altsetting, + windows_clear_halt, + windows_reset_device, + NULL, /* alloc_streams */ + NULL, /* free_streams */ + NULL, /* dev_mem_alloc */ + NULL, /* dev_mem_free */ + NULL, /* kernel_driver_active */ + NULL, /* detach_kernel_driver */ + NULL, /* attach_kernel_driver */ + windows_endpoint_supports_raw_io, + windows_endpoint_set_raw_io, + windows_get_max_raw_io_transfer_size, + windows_destroy_device, + windows_submit_transfer, + windows_cancel_transfer, + NULL, /* clear_transfer_priv */ + NULL, /* handle_events */ + windows_handle_transfer_completion, + sizeof(struct windows_context_priv), + sizeof(union windows_device_priv), + sizeof(struct windows_device_handle_priv), + sizeof(struct windows_transfer_priv), +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.h new file mode 100644 index 00000000..e12b1704 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_common.h @@ -0,0 +1,443 @@ +/* + * Windows backend common header for libusb 1.0 + * + * This file brings together header code common between + * the desktop Windows backends. + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2012 Pete Batard + * Copyright © 2014-2020 Chris Dickens + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_WINDOWS_COMMON_H +#define LIBUSB_WINDOWS_COMMON_H + +#include + +/* + * Workaround for the mess that exists with the DWORD and ULONG types. + * Visual Studio unconditionally defines these types as 'unsigned long' + * and a long is always 32-bits, even on 64-bit builds. GCC on the other + * hand varies the width of a long, matching it to the build. To make + * matters worse, the platform headers for these GCC builds define a + * DWORD/ULONG to be 'unsigned long' on 32-bit builds and 'unsigned int' + * on 64-bit builds. This creates a great deal of warnings for compilers + * that support printf format checking since it will never actually be + * an unsigned long. + */ +#if defined(_MSC_VER) +#define ULONG_CAST(x) (x) +#else +#define ULONG_CAST(x) ((unsigned long)(x)) +#endif + +#if defined(__CYGWIN__) +#define _stricmp strcasecmp +#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 +#endif + +#define safe_free(p) do {if (p != NULL) {free((void *)p); p = NULL;}} while (0) + +/* + * API macros - leveraged from libusb-win32 1.x + */ +#define DLL_STRINGIFY(s) #s + +/* + * Macros for handling DLL themselves + */ +#define DLL_HANDLE_NAME(name) __dll_##name##_handle + +#define DLL_DECLARE_HANDLE(name) \ + static HMODULE DLL_HANDLE_NAME(name) + +#define DLL_GET_HANDLE(ctx, name) \ + do { \ + DLL_HANDLE_NAME(name) = load_system_library(ctx, \ + DLL_STRINGIFY(name)); \ + if (!DLL_HANDLE_NAME(name)) \ + return false; \ + } while (0) + +#define DLL_FREE_HANDLE(name) \ + do { \ + if (DLL_HANDLE_NAME(name)) { \ + FreeLibrary(DLL_HANDLE_NAME(name)); \ + DLL_HANDLE_NAME(name) = NULL; \ + } \ + } while (0) + +/* + * Macros for handling functions within a DLL + */ +#define DLL_FUNC_NAME(name) __dll_##name##_func_t + +#define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args) \ + typedef ret (api * DLL_FUNC_NAME(name))args; \ + static DLL_FUNC_NAME(name) prefixname + +#define DLL_DECLARE_FUNC(api, ret, name, args) \ + DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args) +#define DLL_DECLARE_FUNC_PREFIXED(api, ret, prefix, name, args) \ + DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefix##name, name, args) + +#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ + do { \ + HMODULE h = DLL_HANDLE_NAME(dll); \ + prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \ + DLL_STRINGIFY(name)); \ + if (prefixname) \ + break; \ + prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \ + DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \ + if (prefixname) \ + break; \ + prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \ + DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \ + if (prefixname) \ + break; \ + if (ret_on_failure) \ + return false; \ + } while (0) + +#define DLL_LOAD_FUNC(dll, name, ret_on_failure) \ + DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure) +#define DLL_LOAD_FUNC_PREFIXED(dll, prefix, name, ret_on_failure) \ + DLL_LOAD_FUNC_PREFIXNAME(dll, prefix##name, name, ret_on_failure) + +// https://msdn.microsoft.com/en-us/library/windows/hardware/ff539136(v=vs.85).aspx +#if !defined(USBD_SUCCESS) +typedef LONG USBD_STATUS; + +#define USBD_SUCCESS(Status) ((USBD_STATUS)(Status) >= 0) + +#define USBD_STATUS_STALL_PID ((USBD_STATUS)0xC0000004L) +#define USBD_STATUS_ENDPOINT_HALTED ((USBD_STATUS)0xC0000030L) +#define USBD_STATUS_TIMEOUT ((USBD_STATUS)0xC0006000L) +#define USBD_STATUS_DEVICE_GONE ((USBD_STATUS)0xC0007000L) +#define USBD_STATUS_CANCELED ((USBD_STATUS)0xC0010000L) +#endif + +// error code added with Windows SDK 10.0.18362 +#ifndef ERROR_NO_SUCH_DEVICE +#define ERROR_NO_SUCH_DEVICE 433L +#endif + +/* Windows versions */ +enum windows_version { + WINDOWS_UNDEFINED, + WINDOWS_2000, + WINDOWS_XP, + WINDOWS_2003, // Also XP x64 + WINDOWS_VISTA, + WINDOWS_7, + WINDOWS_8, + WINDOWS_8_1, + WINDOWS_10, + WINDOWS_11, + WINDOWS_12_OR_LATER +}; + +extern enum windows_version windows_version; + +#include + +typedef struct USB_DEVICE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT bcdUSB; + UCHAR bDeviceClass; + UCHAR bDeviceSubClass; + UCHAR bDeviceProtocol; + UCHAR bMaxPacketSize0; + USHORT idVendor; + USHORT idProduct; + USHORT bcdDevice; + UCHAR iManufacturer; + UCHAR iProduct; + UCHAR iSerialNumber; + UCHAR bNumConfigurations; +} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +#include + +#define MAX_DEVICE_ID_LEN 200 + +typedef struct USB_DK_DEVICE_ID { + WCHAR DeviceID[MAX_DEVICE_ID_LEN]; + WCHAR InstanceID[MAX_DEVICE_ID_LEN]; +} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID; + +typedef struct USB_DK_DEVICE_INFO { + USB_DK_DEVICE_ID ID; + ULONG64 FilterID; + ULONG64 Port; + ULONG64 Speed; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; +} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO; + +typedef struct USB_DK_ISO_TRANSFER_RESULT { + ULONG64 ActualLength; + ULONG64 TransferResult; +} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT; + +typedef struct USB_DK_GEN_TRANSFER_RESULT { + ULONG64 BytesTransferred; + ULONG64 UsbdStatus; // USBD_STATUS code +} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT; + +typedef struct USB_DK_TRANSFER_RESULT { + USB_DK_GEN_TRANSFER_RESULT GenResult; + PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT +} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT; + +typedef struct USB_DK_TRANSFER_REQUEST { + ULONG64 EndpointAddress; + PVOID64 Buffer; + ULONG64 BufferLength; + ULONG64 TransferType; + ULONG64 IsochronousPacketsArraySize; + PVOID64 IsochronousPacketsArray; + USB_DK_TRANSFER_RESULT Result; +} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST; + +struct usbdk_device_priv { + USB_DK_DEVICE_ID ID; + PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors; + HANDLE redirector_handle; + HANDLE system_handle; + uint8_t active_configuration; +}; + +struct winusb_device_priv { + bool initialized; +#if defined(LIBUSB_WINDOWS_HOTPLUG) + bool seen_during_scan; // set true for each device encountered during windows_get_device_list + bool seen_before_scan; // set true for each device encountered before windows_get_device_list +#endif + bool root_hub; + uint8_t active_config; + uint16_t langid; // cached USB language ID for string descriptor requests + uint8_t depth; // distance to HCD + const struct windows_usb_api_backend *apib; + char *dev_id; + char *path; // device interface path + int sub_api; // for WinUSB-like APIs + usbi_mutex_t interface_lock; // protects usb_interface[] against concurrent + // claim/release/altsetting from different handles, + // and concurrent enumeration setup + // (set_composite_interface, set_hid_interface, + // and HUB_PASS/DEV_PASS in winusb_get_device_list) + struct { + char *path; // each interface needs a device interface path, + const struct windows_usb_api_backend *apib; // an API backend (multiple drivers support), + int sub_api; + int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) + uint8_t *endpoint; + int current_altsetting; + bool restricted_functionality; // indicates if the interface functionality is restricted + // by Windows (eg. HID keyboards or mice cannot do R/W) + uint8_t num_associated_interfaces; // If non-zero, the interface is part of a grouped + // set of associated interfaces (defined by an IAD) + // and this is the number of interfaces within the + // associated group (bInterfaceCount in IAD). + uint8_t first_associated_interface; // For associated interfaces, this is the index of + // the first interface (bFirstInterface in IAD) for + // the grouped set of associated interfaces. + } 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 +}; + +struct usbdk_transfer_priv { + USB_DK_TRANSFER_REQUEST request; + PULONG64 IsochronousPacketsArray; + PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray; +}; + +struct winusb_transfer_priv { + uint8_t interface_number; + + uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID + uint8_t *hid_dest; // transfer buffer destination, required for HID + size_t hid_expected_size; + + // For isochronous transfers with LibUSBk driver: + void *iso_context; + + // For isochronous transfers with Microsoft WinUSB driver: + void *isoch_buffer_handle; // The isoch_buffer_handle to free at the end of the transfer + BOOL iso_break_stream; // Whether the isoch. stream was to be continued in the last call of libusb_submit_transfer. + // As we this structure is zeroed out upon initialization, we need to use inverse logic here. + libusb_transfer_cb_fn iso_user_callback; // Original transfer callback of the user. Might be used for isochronous transfers. +}; + +struct windows_backend { + int (*init)(struct libusb_context *ctx); + void (*exit)(struct libusb_context *ctx); + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + int (*get_device_string)(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length); + int (*open)(struct libusb_device_handle *dev_handle); + void (*close)(struct libusb_device_handle *dev_handle); + int (*get_active_config_descriptor)(struct libusb_device *device, + void *buffer, size_t len); + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, void *buffer, size_t len); + int (*get_config_descriptor_by_value)(struct libusb_device *device, + uint8_t bConfigurationValue, void **buffer); + int (*get_configuration)(struct libusb_device_handle *dev_handle, uint8_t *config); + int (*set_configuration)(struct libusb_device_handle *dev_handle, uint8_t config); + int (*claim_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); + int (*release_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); + int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, + uint8_t interface_number, uint8_t altsetting); + int (*clear_halt)(struct libusb_device_handle *dev_handle, + unsigned char endpoint); + int (*reset_device)(struct libusb_device_handle *dev_handle); + void (*destroy_device)(struct libusb_device *dev); + int (*submit_transfer)(struct usbi_transfer *itransfer); + int (*cancel_transfer)(struct usbi_transfer *itransfer); + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + enum libusb_transfer_status (*copy_transfer_data)(struct usbi_transfer *itransfer, DWORD length); + int (*endpoint_supports_raw_io)(struct libusb_device_handle *dev_handle, + uint8_t endpoint); + int (*endpoint_set_raw_io)(struct libusb_device_handle *dev_handle, + uint8_t endpoint, int enable); + int (*get_max_raw_io_transfer_size)( + struct libusb_device_handle *dev_handle, + uint8_t endpoint); +}; + +struct windows_context_priv { + const struct windows_backend *backend; + HANDLE completion_port; + HANDLE completion_port_thread; +}; + +union windows_device_priv { + struct usbdk_device_priv usbdk_priv; + struct winusb_device_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); + return &transfer_priv->overlapped; +} + +static inline void set_transfer_priv_handle(struct usbi_transfer *itransfer, HANDLE handle) +{ + struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); + transfer_priv->handle = handle; +} + +static inline struct usbdk_transfer_priv *get_usbdk_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); + return &transfer_priv->usbdk_priv; +} + +static inline struct winusb_transfer_priv *get_winusb_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); + return &transfer_priv->winusb_priv; +} + +extern const struct windows_backend usbdk_backend; +extern const struct windows_backend winusb_backend; + +HMODULE load_system_library(struct libusb_context *ctx, const char *name); +unsigned long htab_hash(const char *str); +enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS status); +void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size); + +#if defined(ENABLE_LOGGING) +const char *windows_error_str(DWORD error_code); +#endif + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.c new file mode 100644 index 00000000..5c898068 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.c @@ -0,0 +1,321 @@ +/* + * windows hotplug backend for libusb 1.0 + * Copyright © 2024 Sylvain Fasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" +#include "threads_windows.h" + +#include "windows_common.h" +#include "windows_hotplug.h" + +#include +#include + +/* The Windows Hotplug system is a two steps process. + * 1. We create a hidden window and listen for DBT_DEVNODES_CHANGED, which Windows + * broadcasts to all top-level windows whenever the device tree changes (no + * registration required). Multiple rapid events (e.g. a hub with many children) + * are coalesced via a short debounce timer so we only scan once the burst settles. + * A maximum delay ceiling guarantees a scan fires within a bounded time even + * during sustained bursts, preventing unbounded latency. + * 2. Upon timer expiry, we snapshot the current device list, run a full re-enumeration + * via the Windows backend, then diff the result: newly found devices that have been + * successfully initialized generate DEVICE_ARRIVED events; devices that were not + * encountered by the re-enumeration (physically removed) generate DEVICE_LEFT events. + */ + +#define HOTPLUG_DEBOUNCE_TIMER_ID 1 +#define HOTPLUG_DEBOUNCE_MS 10 +#define HOTPLUG_DEBOUNCE_MAX_DELAY_MS 100 + +static ULONGLONG first_debounce_tick; +static HWND windows_event_hwnd; +static HANDLE windows_event_thread_handle; +static DWORD WINAPI windows_event_thread_main(LPVOID lpParam); +static LRESULT CALLBACK windows_proc_callback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +#define log_error(operation) do { \ + usbi_err(NULL, "%s failed with error: %s", operation, windows_error_str(0)); \ +} while (0) + +int windows_start_event_monitor(void) +{ + windows_event_thread_handle = CreateThread( + NULL, // Default security descriptor + 0, // Default stack size + windows_event_thread_main, + NULL, // No parameters to pass to the thread + 0, // Start immediately + NULL // No need to keep track of thread ID + ); + + if (windows_event_thread_handle == NULL) + { + log_error("CreateThread"); + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +int windows_stop_event_monitor(void) +{ + if (windows_event_hwnd == NULL) + { + return LIBUSB_SUCCESS; + } + + if (!SUCCEEDED(SendMessage(windows_event_hwnd, WM_CLOSE, 0, 0))) + { + log_error("SendMessage"); + return LIBUSB_ERROR_OTHER; + } + + if (WaitForSingleObject(windows_event_thread_handle, INFINITE) != WAIT_OBJECT_0) + { + log_error("WaitForSingleObject"); + return LIBUSB_ERROR_OTHER; + } + + if (!CloseHandle(windows_event_thread_handle)) + { + log_error("CloseHandle"); + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +static int windows_get_device_list(struct libusb_context *ctx) +{ + // note: context device list is protected by active_contexts_lock + return ((struct windows_context_priv *)usbi_get_context_priv(ctx))->backend->get_device_list(ctx, NULL); +} + +void windows_initial_scan_devices(struct libusb_context *ctx) +{ + usbi_mutex_static_lock(&active_contexts_lock); + + const int ret = windows_get_device_list(ctx); + if (ret != LIBUSB_SUCCESS) + { + usbi_err(ctx, "hotplug failed to retrieve initial list with error: %s", libusb_error_name(ret)); + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +static void windows_refresh_device_list(struct libusb_context *ctx) +{ + struct libusb_device *dev, *next_dev; + + // Step 1: clear seen_during_scan so the scan can mark which devices are still + // physically present, and set seen_before_scan so we can distinguish newly + // created devices (which start with seen_before_scan=false via calloc). + for_each_device_safe(ctx, dev, next_dev) + { + struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev); + priv->seen_during_scan = false; + priv->seen_before_scan = true; + } + + // Step 2: re-enumerate — winusb_get_device_list attaches newly-arrived devices + // and sets seen_during_scan=true for every device it physically encounters. + // seen_before_scan is untouched and will be left in default state (false) for newly created devices, allowing us to identify them in the next step. + const int ret = windows_get_device_list(ctx); + if (ret != LIBUSB_SUCCESS) + { + usbi_err(ctx, "hotplug failed to retrieve current list with error: %s", libusb_error_name(ret)); + return; + } + + // Step 3: diff old vs new. + for_each_device_safe(ctx, dev, next_dev) + { + struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev); + + if (!priv->seen_during_scan) + { + // Not encountered by the scan: device was physically removed. + if (priv->initialized) + { + usbi_disconnect_device(dev); // fires DEVICE_LEFT + } + else + { + usbi_detach_device(dev); + // No DEVICE_LEFT message is posted for uninitialized + // devices, so no message handler will drop the initial + // ref. We must drop it here to avoid a leak. + libusb_unref_device(dev); + } + } + else if (!priv->seen_before_scan) + { + if (priv->initialized) + { + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); + } + } + } +} + +static void windows_refresh_device_list_for_all_ctx(void) +{ + usbi_mutex_static_lock(&active_contexts_lock); + + struct libusb_context *ctx; + for_each_context(ctx) + { + windows_refresh_device_list(ctx); + } + + usbi_mutex_static_unlock(&active_contexts_lock); +} + +#define WND_CLASS_NAME TEXT("libusb-1.0-windows-hotplug") + +static bool init_wnd_class(void) +{ + WNDCLASS wndClass = { 0 }; + wndClass.lpfnWndProc = windows_proc_callback; + wndClass.hInstance = GetModuleHandle(NULL); + wndClass.lpszClassName = WND_CLASS_NAME; + + if (!RegisterClass(&wndClass)) + { + log_error("event thread: RegisterClass"); + return false; + } + + return true; +} + +static DWORD WINAPI windows_event_thread_main(LPVOID lpParam) +{ + UNUSED(lpParam); + + usbi_dbg(NULL, "windows event thread entering"); + + if (!init_wnd_class()) + { + return (DWORD)-1; + } + + windows_event_hwnd = CreateWindow( + WND_CLASS_NAME, + TEXT(""), + 0, + 0, 0, 0, 0, + NULL, NULL, + GetModuleHandle(NULL), + NULL); + + if (windows_event_hwnd == NULL) + { + log_error("event thread: CreateWindow"); + return (DWORD)-1; + } + + MSG msg; + BOOL ret_val; + + while ((ret_val = GetMessage(&msg, windows_event_hwnd, 0, 0)) != 0) + { + if (ret_val == -1) + { + log_error("event thread: GetMessage"); + break; + } + + if (!SUCCEEDED(TranslateMessage(&msg))) + { + log_error("event thread: TranslateMessage"); + } + + if (!SUCCEEDED(DispatchMessage(&msg))) + { + log_error("event thread: DispatchMessage"); + } + } + + usbi_dbg(NULL, "windows event thread exiting"); + + return 0; +} + +static LRESULT CALLBACK windows_proc_callback( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_DEVICECHANGE: + if (wParam == DBT_DEVNODES_CHANGED) + { + if (first_debounce_tick == 0) + { + // First event in a new burst — record the time and start the debounce timer. + first_debounce_tick = GetTickCount64(); + SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL); + } + else if (GetTickCount64() - first_debounce_tick >= HOTPLUG_DEBOUNCE_MAX_DELAY_MS) + { + // Maximum delay reached — force an immediate scan so devices + // are not invisible for the entire duration of a sustained burst. + KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); + first_debounce_tick = 0; + windows_refresh_device_list_for_all_ctx(); + } + else + { + // Still within the max-delay window — reset the debounce timer. + SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL); + } + return TRUE; + } + return DefWindowProc(hwnd, message, wParam, lParam); + + case WM_TIMER: + if (wParam == HOTPLUG_DEBOUNCE_TIMER_ID) + { + KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); + first_debounce_tick = 0; + windows_refresh_device_list_for_all_ctx(); + return 0; + } + return DefWindowProc(hwnd, message, wParam, lParam); + + case WM_CLOSE: + KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); + first_debounce_tick = 0; + if (!DestroyWindow(hwnd)) + { + log_error("DestroyWindow"); + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.h new file mode 100644 index 00000000..11add25c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_hotplug.h @@ -0,0 +1,28 @@ +/* + * windows hotplug backend for libusb 1.0 + * Copyright © 2024 Sylvain Fasel + * + * 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 WINDOWS_HOTPLUG_H +#define WINDOWS_HOTPLUG_H + +int windows_start_event_monitor(void); +int windows_stop_event_monitor(void); + +void windows_initial_scan_devices(struct libusb_context *ctx); + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.c new file mode 100644 index 00000000..c13deeb0 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.c @@ -0,0 +1,732 @@ +/* + * windows UsbDk backend for libusb 1.0 + * Copyright © 2014 Red Hat, Inc. + + * Authors: + * Dmitry Fleytman + * Pavel Gurvich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include "libusbi.h" +#include "windows_usbdk.h" + +#if !defined(STATUS_SUCCESS) +typedef LONG NTSTATUS; +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +#if !defined(STATUS_CANCELLED) +#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) +#endif + +#if !defined(STATUS_REQUEST_CANCELED) +#define STATUS_REQUEST_CANCELED ((NTSTATUS)0xC0000703L) +#endif + +static struct { + HMODULE module; + + USBDK_GET_DEVICES_LIST GetDevicesList; + USBDK_RELEASE_DEVICES_LIST ReleaseDevicesList; + USBDK_START_REDIRECT StartRedirect; + USBDK_STOP_REDIRECT StopRedirect; + USBDK_GET_CONFIGURATION_DESCRIPTOR GetConfigurationDescriptor; + USBDK_RELEASE_CONFIGURATION_DESCRIPTOR ReleaseConfigurationDescriptor; + USBDK_READ_PIPE ReadPipe; + USBDK_WRITE_PIPE WritePipe; + USBDK_ABORT_PIPE AbortPipe; + USBDK_RESET_PIPE ResetPipe; + USBDK_SET_ALTSETTING SetAltsetting; + USBDK_RESET_DEVICE ResetDevice; + USBDK_GET_REDIRECTOR_SYSTEM_HANDLE GetRedirectorSystemHandle; +} usbdk_helper; + +static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name) +{ + FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name); + + if (api_ptr == NULL) + usbi_err(ctx, "UsbDkHelper API %s not found: %s", api_name, windows_error_str(0)); + + return api_ptr; +} + +static void unload_usbdk_helper_dll(void) +{ + if (usbdk_helper.module != NULL) { + FreeLibrary(usbdk_helper.module); + usbdk_helper.module = NULL; + } +} + +static int load_usbdk_helper_dll(struct libusb_context *ctx) +{ + usbdk_helper.module = load_system_library(ctx, "UsbDkHelper"); + if (usbdk_helper.module == NULL) { + usbi_err(ctx, "Failed to load UsbDkHelper.dll: %s", windows_error_str(0)); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbdk_helper.GetDevicesList = (USBDK_GET_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_GetDevicesList"); + if (usbdk_helper.GetDevicesList == NULL) + goto error_unload; + + usbdk_helper.ReleaseDevicesList = (USBDK_RELEASE_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseDevicesList"); + if (usbdk_helper.ReleaseDevicesList == NULL) + goto error_unload; + + usbdk_helper.StartRedirect = (USBDK_START_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StartRedirect"); + if (usbdk_helper.StartRedirect == NULL) + goto error_unload; + + usbdk_helper.StopRedirect = (USBDK_STOP_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StopRedirect"); + if (usbdk_helper.StopRedirect == NULL) + goto error_unload; + + usbdk_helper.GetConfigurationDescriptor = (USBDK_GET_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_GetConfigurationDescriptor"); + if (usbdk_helper.GetConfigurationDescriptor == NULL) + goto error_unload; + + usbdk_helper.ReleaseConfigurationDescriptor = (USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseConfigurationDescriptor"); + if (usbdk_helper.ReleaseConfigurationDescriptor == NULL) + goto error_unload; + + usbdk_helper.ReadPipe = (USBDK_READ_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ReadPipe"); + if (usbdk_helper.ReadPipe == NULL) + goto error_unload; + + usbdk_helper.WritePipe = (USBDK_WRITE_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_WritePipe"); + if (usbdk_helper.WritePipe == NULL) + goto error_unload; + + usbdk_helper.AbortPipe = (USBDK_ABORT_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_AbortPipe"); + if (usbdk_helper.AbortPipe == NULL) + goto error_unload; + + usbdk_helper.ResetPipe = (USBDK_RESET_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ResetPipe"); + if (usbdk_helper.ResetPipe == NULL) + goto error_unload; + + usbdk_helper.SetAltsetting = (USBDK_SET_ALTSETTING)get_usbdk_proc_addr(ctx, "UsbDk_SetAltsetting"); + if (usbdk_helper.SetAltsetting == NULL) + goto error_unload; + + usbdk_helper.ResetDevice = (USBDK_RESET_DEVICE)get_usbdk_proc_addr(ctx, "UsbDk_ResetDevice"); + if (usbdk_helper.ResetDevice == NULL) + goto error_unload; + + usbdk_helper.GetRedirectorSystemHandle = (USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)get_usbdk_proc_addr(ctx, "UsbDk_GetRedirectorSystemHandle"); + if (usbdk_helper.GetRedirectorSystemHandle == NULL) + goto error_unload; + + return LIBUSB_SUCCESS; + +error_unload: + FreeLibrary(usbdk_helper.module); + usbdk_helper.module = NULL; + return LIBUSB_ERROR_NOT_FOUND; +} + +typedef SC_HANDLE (WINAPI *POPENSCMANAGERA)(LPCSTR, LPCSTR, DWORD); +typedef SC_HANDLE (WINAPI *POPENSERVICEA)(SC_HANDLE, LPCSTR, DWORD); +typedef BOOL (WINAPI *PCLOSESERVICEHANDLE)(SC_HANDLE); + +static int usbdk_init(struct libusb_context *ctx) +{ + POPENSCMANAGERA pOpenSCManagerA; + POPENSERVICEA pOpenServiceA; + PCLOSESERVICEHANDLE pCloseServiceHandle; + SC_HANDLE managerHandle; + SC_HANDLE serviceHandle; + HMODULE h; + + h = load_system_library(ctx, "Advapi32"); + if (h == NULL) { + usbi_warn(ctx, "failed to open Advapi32\n"); + return LIBUSB_ERROR_OTHER; + } + + pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(h, "OpenSCManagerA"); + if (pOpenSCManagerA == NULL) { + usbi_warn(ctx, "failed to find %s in Advapi32\n", "OpenSCManagerA"); + goto error_free_library; + } + pOpenServiceA = (POPENSERVICEA)GetProcAddress(h, "OpenServiceA"); + if (pOpenServiceA == NULL) { + usbi_warn(ctx, "failed to find %s in Advapi32\n", "OpenServiceA"); + goto error_free_library; + } + pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(h, "CloseServiceHandle"); + if (pCloseServiceHandle == NULL) { + usbi_warn(ctx, "failed to find %s in Advapi32\n", "CloseServiceHandle"); + goto error_free_library; + } + + managerHandle = pOpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT); + if (managerHandle == NULL) { + usbi_warn(ctx, "failed to open service control manager: %s", windows_error_str(0)); + goto error_free_library; + } + + serviceHandle = pOpenServiceA(managerHandle, "UsbDk", GENERIC_READ); + pCloseServiceHandle(managerHandle); + + if (serviceHandle == NULL) { + if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST) + usbi_warn(ctx, "failed to open UsbDk service: %s", windows_error_str(0)); + FreeLibrary(h); + return LIBUSB_ERROR_NOT_FOUND; + } + + pCloseServiceHandle(serviceHandle); + FreeLibrary(h); + + return load_usbdk_helper_dll(ctx); + +error_free_library: + FreeLibrary(h); + return LIBUSB_ERROR_OTHER; +} + +static void usbdk_exit(struct libusb_context *ctx) +{ + UNUSED(ctx); + unload_usbdk_helper_dll(); +} + +static int usbdk_get_session_id_for_device(struct libusb_context *ctx, + PUSB_DK_DEVICE_ID id, unsigned long *session_id) +{ + char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID) + 1]; + + if (snprintf(dev_identity, sizeof(dev_identity), "%S%S", id->DeviceID, id->InstanceID) == -1) { + usbi_warn(ctx, "cannot form device identity"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + *session_id = htab_hash(dev_identity); + + return LIBUSB_SUCCESS; +} + +static void usbdk_release_config_descriptors(struct usbdk_device_priv *priv, uint8_t count) +{ + uint8_t i; + + for (i = 0; i < count; i++) + usbdk_helper.ReleaseConfigurationDescriptor(priv->config_descriptors[i]); + + free(priv->config_descriptors); + priv->config_descriptors = NULL; +} + +static int usbdk_cache_config_descriptors(struct libusb_context *ctx, + struct usbdk_device_priv *priv, PUSB_DK_DEVICE_INFO info) +{ + uint8_t i; + USB_DK_CONFIG_DESCRIPTOR_REQUEST Request; + Request.ID = info->ID; + + priv->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + if (priv->config_descriptors == NULL) { + usbi_err(ctx, "failed to allocate configuration descriptors holder"); + return LIBUSB_ERROR_NO_MEM; + } + + for (i = 0; i < info->DeviceDescriptor.bNumConfigurations; i++) { + ULONG Length; + + Request.Index = i; + if (!usbdk_helper.GetConfigurationDescriptor(&Request, &priv->config_descriptors[i], &Length)) { + usbi_err(ctx, "failed to retrieve configuration descriptors"); + usbdk_release_config_descriptors(priv, i); + return LIBUSB_ERROR_OTHER; + } + } + + return LIBUSB_SUCCESS; +} + +static inline int usbdk_device_priv_init(struct libusb_context *ctx, struct libusb_device *dev, PUSB_DK_DEVICE_INFO info) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev); + + priv->ID = info->ID; + priv->active_configuration = 0; + + return usbdk_cache_config_descriptors(ctx, priv, info); +} + +static void usbdk_device_init(struct libusb_device *dev, PUSB_DK_DEVICE_INFO info) +{ + dev->bus_number = (uint8_t)info->FilterID; + dev->port_number = (uint8_t)info->Port; + dev->parent_dev = NULL; + + // Addresses in libusb are 1-based + dev->device_address = (uint8_t)(info->Port + 1); + + static_assert(sizeof(dev->device_descriptor) == sizeof(info->DeviceDescriptor), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + switch (info->Speed) { + case LowSpeed: + dev->speed = LIBUSB_SPEED_LOW; + break; + case FullSpeed: + dev->speed = LIBUSB_SPEED_FULL; + break; + case HighSpeed: + dev->speed = LIBUSB_SPEED_HIGH; + break; + case SuperSpeed: + dev->speed = LIBUSB_SPEED_SUPER; + break; + case NoSpeed: + default: + dev->speed = LIBUSB_SPEED_UNKNOWN; + break; + } +} + +static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + int r = LIBUSB_SUCCESS; + ULONG i; + struct discovered_devs *discdevs = NULL; + ULONG dev_number; + PUSB_DK_DEVICE_INFO devices; + + if (!usbdk_helper.GetDevicesList(&devices, &dev_number)) + return LIBUSB_ERROR_OTHER; + + for (i = 0; i < dev_number; i++) { + unsigned long session_id; + struct libusb_device *dev = NULL; + + if (usbdk_get_session_id_for_device(ctx, &devices[i].ID, &session_id)) + continue; + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + usbi_err(ctx, "failed to allocate a new device structure"); + continue; + } + + usbdk_device_init(dev, &devices[i]); + if (usbdk_device_priv_init(ctx, dev, &devices[i]) != LIBUSB_SUCCESS) { + libusb_unref_device(dev); + continue; + } + + usbi_connect_device(dev); + } + + if (_discdevs) { + discdevs = discovered_devs_append(*_discdevs, dev); + libusb_unref_device(dev); + if (!discdevs) { + usbi_err(ctx, "cannot append new device to list"); + r = LIBUSB_ERROR_NO_MEM; + goto func_exit; + } + + *_discdevs = discdevs; + } + } + +func_exit: + usbdk_helper.ReleaseDevicesList(devices); + return r; +} + +static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptors[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, config_header, size); + return (int)size; +} + +static int usbdk_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, + void **buffer) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + uint8_t index; + + for (index = 0; index < dev->device_descriptor.bNumConfigurations; index++) { + config_header = priv->config_descriptors[index]; + if (config_header->bConfigurationValue == bConfigurationValue) { + *buffer = priv->config_descriptors[index]; + return (int)config_header->wTotalLength; + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +static int usbdk_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev); + + return usbdk_get_config_descriptor(dev, priv->active_configuration, buffer, len); +} + +static int usbdk_open(struct libusb_device_handle *dev_handle) +{ + struct libusb_device *dev = dev_handle->dev; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + struct usbdk_device_priv *device_priv = usbi_get_device_priv(dev); + + device_priv->redirector_handle = usbdk_helper.StartRedirect(&device_priv->ID); + if (device_priv->redirector_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "Redirector startup failed"); + device_priv->redirector_handle = NULL; + return LIBUSB_ERROR_OTHER; + } + + device_priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(device_priv->redirector_handle); + + 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; + device_priv->redirector_handle = NULL; + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +static void usbdk_close(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (!usbdk_helper.StopRedirect(priv->redirector_handle)) + usbi_err(HANDLE_CTX(dev_handle), "Redirector shutdown failed"); + + priv->system_handle = NULL; + priv->redirector_handle = NULL; +} + +static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + *config = priv->active_configuration; + + return LIBUSB_SUCCESS; +} + +static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, uint8_t config) +{ + UNUSED(dev_handle); + UNUSED(config); + return LIBUSB_SUCCESS; +} + +static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) { + usbi_err(HANDLE_CTX(dev_handle), "SetAltsetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) { + usbi_err(HANDLE_CTX(dev_handle), "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_reset_device(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetDevice(priv->redirector_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "ResetDevice failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static void usbdk_destroy_device(struct libusb_device *dev) +{ + struct usbdk_device_priv *priv = usbi_get_device_priv(dev); + + if (priv->config_descriptors != NULL) + usbdk_release_config_descriptors(priv, dev->device_descriptor.bNumConfigurations); +} + +static void usbdk_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + safe_free(transfer_priv->IsochronousPacketsArray); + safe_free(transfer_priv->IsochronousResultsArray); + } +} + +static int usbdk_do_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer); + OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer); + TransferResult transResult; + + transfer_priv->request.Buffer = (PVOID64)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.TransferType = ControlTransferType; + + set_transfer_priv_handle(itransfer, priv->system_handle); + + if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) + transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped); + else + transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped); + + switch (transResult) { + case TransferSuccess: + windows_force_sync_completion(itransfer, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred); + break; + case TransferSuccessAsync: + break; + case TransferFailure: + usbi_err(TRANSFER_CTX(transfer), "ControlTransfer failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer); + OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer); + TransferResult transferRes; + + transfer_priv->request.Buffer = (PVOID64)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + transfer_priv->request.TransferType = BulkTransferType; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + transfer_priv->request.TransferType = InterruptTransferType; + break; + } + + set_transfer_priv_handle(itransfer, priv->system_handle); + + if (IS_XFERIN(transfer)) + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped); + else + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped); + + switch (transferRes) { + case TransferSuccess: + windows_force_sync_completion(itransfer, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred); + break; + case TransferSuccessAsync: + break; + case TransferFailure: + usbi_err(TRANSFER_CTX(transfer), "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer); + OVERLAPPED *overlapped = get_transfer_priv_overlapped(itransfer); + TransferResult transferRes; + int i; + + transfer_priv->request.Buffer = (PVOID64)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + transfer_priv->request.TransferType = IsochronousTransferType; + transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets; + transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64)); + transfer_priv->request.IsochronousPacketsArray = (PVOID64)transfer_priv->IsochronousPacketsArray; + if (!transfer_priv->IsochronousPacketsArray) { + usbi_err(TRANSFER_CTX(transfer), "Allocation of IsochronousPacketsArray failed"); + return LIBUSB_ERROR_NO_MEM; + } + + transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT)); + transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)transfer_priv->IsochronousResultsArray; + if (!transfer_priv->IsochronousResultsArray) { + usbi_err(TRANSFER_CTX(transfer), "Allocation of isochronousResultsArray failed"); + return LIBUSB_ERROR_NO_MEM; + } + + for (i = 0; i < transfer->num_iso_packets; i++) + transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length; + + set_transfer_priv_handle(itransfer, priv->system_handle); + + if (IS_XFERIN(transfer)) + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped); + else + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped); + + switch (transferRes) { + case TransferSuccess: + windows_force_sync_completion(itransfer, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred); + break; + case TransferSuccessAsync: + break; + case TransferFailure: + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return usbdk_do_control_transfer(itransfer); + 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; //TODO: Check whether we can support this in UsbDk + return usbdk_do_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return usbdk_do_iso_transfer(itransfer); + default: + // Should not get here since windows_submit_transfer() validates + // the transfer->type field + usbi_err(TRANSFER_CTX(transfer), "unsupported endpoint type %d", transfer->type); + return LIBUSB_ERROR_NOT_SUPPORTED; + } +} + +static enum libusb_transfer_status usbdk_copy_transfer_data(struct usbi_transfer *itransfer, DWORD length) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_transfer_priv *transfer_priv = get_usbdk_transfer_priv(itransfer); + + UNUSED(length); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + ULONG64 i; + + for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) { + struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; + + switch (transfer_priv->IsochronousResultsArray[i].TransferResult) { + case STATUS_SUCCESS: + case STATUS_CANCELLED: + case STATUS_REQUEST_CANCELED: + lib_desc->status = LIBUSB_TRANSFER_COMPLETED; // == ERROR_SUCCESS + break; + default: + lib_desc->status = LIBUSB_TRANSFER_ERROR; // ERROR_UNKNOWN_EXCEPTION; + break; + } + + lib_desc->actual_length = (unsigned int)transfer_priv->IsochronousResultsArray[i].ActualLength; + } + } + + itransfer->transferred += (int)transfer_priv->request.Result.GenResult.BytesTransferred; + return usbd_status_to_libusb_transfer_status((USBD_STATUS)transfer_priv->request.Result.GenResult.UsbdStatus); +} + +const struct windows_backend usbdk_backend = { + usbdk_init, + usbdk_exit, + usbdk_get_device_list, + NULL, /* usbdk_get_device_string */ + usbdk_open, + usbdk_close, + usbdk_get_active_config_descriptor, + usbdk_get_config_descriptor, + usbdk_get_config_descriptor_by_value, + usbdk_get_configuration, + usbdk_set_configuration, + usbdk_claim_interface, + usbdk_release_interface, + usbdk_set_interface_altsetting, + usbdk_clear_halt, + usbdk_reset_device, + usbdk_destroy_device, + usbdk_submit_transfer, + NULL, /* cancel_transfer */ + usbdk_clear_transfer_priv, + usbdk_copy_transfer_data, + NULL, /* endpoint_supports_raw_io */ + NULL, /* endpoint_set_raw_io */ + NULL, /* get_max_raw_io_transfer_size */ +}; diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.h new file mode 100644 index 00000000..0eb3779a --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_usbdk.h @@ -0,0 +1,106 @@ +/* +* windows UsbDk backend for libusb 1.0 +* Copyright © 2014 Red Hat, Inc. + +* Authors: +* Dmitry Fleytman +* Pavel Gurvich +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef LIBUSB_WINDOWS_USBDK_H +#define LIBUSB_WINDOWS_USBDK_H + +#include "windows_common.h" + +typedef struct USB_DK_CONFIG_DESCRIPTOR_REQUEST { + USB_DK_DEVICE_ID ID; + ULONG64 Index; +} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST; + +typedef enum { + TransferFailure = 0, + TransferSuccess, + TransferSuccessAsync +} TransferResult; + +typedef enum { + NoSpeed = 0, + LowSpeed, + FullSpeed, + HighSpeed, + SuperSpeed +} USB_DK_DEVICE_SPEED; + +typedef enum { + ControlTransferType, + BulkTransferType, + InterruptTransferType, + IsochronousTransferType +} USB_DK_TRANSFER_TYPE; + +typedef BOOL (__cdecl *USBDK_GET_DEVICES_LIST)( + PUSB_DK_DEVICE_INFO *DeviceInfo, + PULONG DeviceNumber +); +typedef void (__cdecl *USBDK_RELEASE_DEVICES_LIST)( + PUSB_DK_DEVICE_INFO DeviceInfo +); +typedef HANDLE (__cdecl *USBDK_START_REDIRECT)( + PUSB_DK_DEVICE_ID DeviceId +); +typedef BOOL (__cdecl *USBDK_STOP_REDIRECT)( + HANDLE DeviceHandle +); +typedef BOOL (__cdecl *USBDK_GET_CONFIGURATION_DESCRIPTOR)( + PUSB_DK_CONFIG_DESCRIPTOR_REQUEST Request, + PUSB_CONFIGURATION_DESCRIPTOR *Descriptor, + PULONG Length +); +typedef void (__cdecl *USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)( + PUSB_CONFIGURATION_DESCRIPTOR Descriptor +); +typedef TransferResult (__cdecl *USBDK_WRITE_PIPE)( + HANDLE DeviceHandle, + PUSB_DK_TRANSFER_REQUEST Request, + LPOVERLAPPED lpOverlapped +); +typedef TransferResult (__cdecl *USBDK_READ_PIPE)( + HANDLE DeviceHandle, + PUSB_DK_TRANSFER_REQUEST Request, + LPOVERLAPPED lpOverlapped +); +typedef BOOL (__cdecl *USBDK_ABORT_PIPE)( + HANDLE DeviceHandle, + ULONG64 PipeAddress +); +typedef BOOL (__cdecl *USBDK_RESET_PIPE)( + HANDLE DeviceHandle, + ULONG64 PipeAddress +); +typedef BOOL (__cdecl *USBDK_SET_ALTSETTING)( + HANDLE DeviceHandle, + ULONG64 InterfaceIdx, + ULONG64 AltSettingIdx +); +typedef BOOL (__cdecl *USBDK_RESET_DEVICE)( + HANDLE DeviceHandle +); +typedef HANDLE (__cdecl *USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)( + HANDLE DeviceHandle +); + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.c new file mode 100644 index 00000000..b84c4ac9 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.c @@ -0,0 +1,5478 @@ +/* + * windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * Copyright © 2016-2018 Chris Dickens + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * Hash table functions adapted from glibc, by Ulrich Drepper et al. + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "windows_winusb.h" + +#define HANDLE_VALID(h) (((h) != NULL) && ((h) != INVALID_HANDLE_VALUE)) + +// The below macro is used in conjunction with safe loops. +#define LOOP_BREAK(err) \ + { \ + r = err; \ + continue; \ + } + +static int interface_by_endpoint(struct winusb_device_priv *priv, + struct winusb_device_handle_priv *handle_priv, uint8_t endpoint_address); +// WinUSB-like API prototypes +static bool winusbx_init(struct libusb_context *ctx); +static void winusbx_exit(void); +static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle); +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting); +static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int winusbx_cancel_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static enum libusb_transfer_status winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length); +static int winusbx_endpoint_supports_raw_io(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint); +static int winusbx_endpoint_set_raw_io(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint, int enable); +static int winusbx_get_max_raw_io_transfer_size(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint); +// HID API prototypes +static bool hid_init(struct libusb_context *ctx); +static void hid_exit(void); +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle); +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting); +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static enum libusb_transfer_status hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length); +// Composite API prototypes +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle); +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting); +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_cancel_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static enum libusb_transfer_status composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length); +static int composite_endpoint_supports_raw_io(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint); +static int composite_endpoint_set_raw_io(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint, int enable); +static int composite_get_max_raw_io_transfer_size(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint); + +static usbi_mutex_t autoclaim_lock; + +// API globals +static struct winusb_interface WinUSBX[SUB_API_MAX]; +#define CHECK_WINUSBX_AVAILABLE(sub_api) \ + do { \ + if (sub_api == SUB_API_NOTSET) \ + sub_api = priv->sub_api; \ + if (WinUSBX[sub_api].hDll == NULL) \ + return LIBUSB_ERROR_ACCESS; \ + } while (0) + +#define CHECK_HID_AVAILABLE \ + do { \ + if (DLL_HANDLE_NAME(hid) == NULL) \ + return LIBUSB_ERROR_ACCESS; \ + } while (0) + +#if defined(ENABLE_LOGGING) +static const char *guid_to_string(const GUID *guid, char guid_string[MAX_GUID_STRING_LENGTH]) +{ + 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, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + + return guid_string; +} +#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 + */ +static char *normalize_path(const char *path) +{ + char *ret_path = _strdup(path); + char *p; + + if (ret_path == NULL) + return NULL; + + for (p = ret_path; *p != '\0'; p++) + *p = (char)toupper((unsigned char)*p); + + return ret_path; +} + +/* + * Cfgmgr32, AdvAPI32, OLE32 and SetupAPI DLL functions + */ +static bool init_dlls(struct libusb_context *ctx) +{ + DLL_GET_HANDLE(ctx, Cfgmgr32); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, true); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, true); + + // Prefixed to avoid conflict with header files + DLL_GET_HANDLE(ctx, AdvAPI32); + DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExA, true); + DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, true); + + DLL_GET_HANDLE(ctx, SetupAPI); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInstanceIdA, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, true); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, true); + + return true; +} + +static void exit_dlls(void) +{ + DLL_FREE_HANDLE(SetupAPI); + DLL_FREE_HANDLE(AdvAPI32); + DLL_FREE_HANDLE(Cfgmgr32); +} + +/* + * enumerate interfaces for the whole USB class + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * enumerator: the generic USB class for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static bool get_devinfo_data(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *enumerator, unsigned _index) +{ + if (_index == 0) { + *dev_info = pSetupDiGetClassDevsA(NULL, enumerator, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); + if (*dev_info == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not obtain device info set for PnP enumerator '%s': %s", + enumerator, windows_error_str(0)); + return false; + } + } + + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "could not obtain device info data for PnP enumerator '%s' index %u: %s", + enumerator, _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return false; + } + return true; +} + +/* + * enumerate interfaces for a specific GUID + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * guid: the GUID for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info, + PSP_DEVINFO_DATA dev_info_data, LPCGUID guid, DWORD *_index, char **dev_interface_path) +{ + 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; + +#ifndef ENABLE_LOGGING + UNUSED(*guid_string); +#endif + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for (;;) { + 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, guid_string), ULONG_CAST(*_index), windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + // No more devices + return LIBUSB_SUCCESS; + } + + // Always advance the index for the next iteration + (*_index)++; + + if (pSetupDiEnumDeviceInterfaces(dev_info, dev_info_data, guid, 0, &dev_interface_data)) + break; + + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for %s devInst %lX: %s", + guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + + // Device does not have an interface matching this GUID, skip + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for %s devInst %lX: %s", + guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong"); + return LIBUSB_ERROR_OTHER; + } + + 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, guid_string), ULONG_CAST(dev_info_data->DevInst)); + return LIBUSB_ERROR_NO_MEM; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + 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, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0)); + free(dev_interface_details); + return LIBUSB_ERROR_OTHER; + } + + *dev_interface_path = normalize_path(dev_interface_details->DevicePath); + free(dev_interface_details); + + if (*dev_interface_path == NULL) { + usbi_err(ctx, "could not allocate interface path for %s devInst %lX", + guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst)); + return LIBUSB_ERROR_NO_MEM; + } + + return LIBUSB_SUCCESS; +} + +/* For libusb0 filter */ +static int get_interface_details_filter(struct libusb_context *ctx, HDEVINFO *dev_info, + DWORD _index, char *filter_path, char **dev_interface_path) +{ + const GUID *libusb0_guid = &GUID_DEVINTERFACE_LIBUSB0_FILTER; + SP_DEVICE_INTERFACE_DATA dev_interface_data; + PSP_DEVICE_INTERFACE_DETAIL_DATA_A dev_interface_details; + HKEY hkey_dev_interface; + DWORD size; + int err = LIBUSB_ERROR_OTHER; + + if (_index == 0) { + *dev_info = pSetupDiGetClassDevsA(libusb0_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (*dev_info == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not obtain device info set: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, libusb0_guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %lu: %s", + ULONG_CAST(_index), windows_error_str(0)); + goto err_exit; + } + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return LIBUSB_SUCCESS; + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %lu: %s", + ULONG_CAST(_index), windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong"); + goto err_exit; + } + + dev_interface_details = malloc(size); + if (dev_interface_details == NULL) { + usbi_err(ctx, "could not allocate interface data for index %lu", ULONG_CAST(_index)); + err = LIBUSB_ERROR_NO_MEM; + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, dev_interface_details, size, NULL, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %lu: %s", + ULONG_CAST(_index), windows_error_str(0)); + free(dev_interface_details); + goto err_exit; + } + + *dev_interface_path = normalize_path(dev_interface_details->DevicePath); + free(dev_interface_details); + + if (*dev_interface_path == NULL) { + usbi_err(ctx, "could not allocate interface path for index %lu", ULONG_CAST(_index)); + err = LIBUSB_ERROR_NO_MEM; + goto err_exit; + } + + // [trobinso] lookup the libusb0 symbolic index. + hkey_dev_interface = pSetupDiOpenDeviceInterfaceRegKey(*dev_info, &dev_interface_data, 0, KEY_READ); + if (hkey_dev_interface != INVALID_HANDLE_VALUE) { + DWORD libusb0_symboliclink_index = 0; + DWORD value_length = sizeof(DWORD); + LONG status; + + 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(ctx, "assigned libusb0 symbolic link %s", filter_path); + } else { + // libusb0.sys was connected to this device instance at one time; but not anymore. + } + } + pRegCloseKey(hkey_dev_interface); + } else { + usbi_warn(ctx, "could not open device interface registry key for index %lu: %s", + ULONG_CAST(_index), windows_error_str(0)); + // TODO: should this be an error? + } + + return LIBUSB_SUCCESS; + +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return err; +} + +/* + * Returns the first known ancestor of a device + */ +static struct libusb_device *get_ancestor(struct libusb_context *ctx, + DEVINST devinst, PDEVINST _parent_devinst) +{ + struct libusb_device *dev = NULL; + DEVINST parent_devinst; + + while (dev == NULL) { + if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS) + break; + devinst = parent_devinst; + dev = usbi_get_device_by_session_id(ctx, (unsigned long)devinst); + } + + if ((dev != NULL) && (_parent_devinst != NULL)) + *_parent_devinst = devinst; + + return dev; +} + +/* + * Determine which interface the given endpoint address belongs to + */ +static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, uint8_t ep) +{ + const struct libusb_interface *intf; + const struct libusb_interface_descriptor *intf_desc; + uint8_t i, k; + int j; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + intf = &conf_desc->interface[i]; + for (j = 0; j < intf->num_altsetting; j++) { + intf_desc = &intf->altsetting[j]; + for (k = 0; k < intf_desc->bNumEndpoints; k++) { + if (intf_desc->endpoint[k].bEndpointAddress == ep) { + usbi_dbg(NULL, "found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i); + return intf_desc->bInterfaceNumber; + } + } + } + } + + usbi_dbg(NULL, "endpoint %02X not found on any interface", ep); + return LIBUSB_ERROR_NOT_FOUND; +} + +static const struct libusb_interface_descriptor *get_interface_descriptor_by_number(struct libusb_device_handle *dev_handle, struct libusb_config_descriptor *conf_desc, int iface, uint8_t altsetting) +{ + int i; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + if (altsetting < conf_desc->interface[i].num_altsetting && conf_desc->interface[i].altsetting[altsetting].bInterfaceNumber == iface) { + return &conf_desc->interface[i].altsetting[altsetting]; + } + } + + usbi_err(HANDLE_CTX(dev_handle), "interface %d with altsetting %d not found for device", iface, (int)altsetting); + return NULL; +} + +/* + * Open a device and associate the HANDLE with the context's I/O completion port + */ +static HANDLE windows_open(struct libusb_device_handle *dev_handle, const char *path, DWORD access) +{ + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + struct windows_context_priv *priv = usbi_get_context_priv(ctx); + HANDLE handle; + + handle = CreateFileA(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) + return handle; + + 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; + } + + return handle; +} + +/* + * Populate the endpoints addresses of the device_priv interface helper structs + */ +static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + struct libusb_config_descriptor *conf_desc; + const struct libusb_interface_descriptor *if_desc; + int i, r; + + r = libusb_get_active_config_descriptor(dev_handle->dev, &conf_desc); + if (r != LIBUSB_SUCCESS) { + usbi_warn(HANDLE_CTX(dev_handle), "could not read config descriptor: error %d", r); + return r; + } + + if_desc = get_interface_descriptor_by_number(dev_handle, conf_desc, iface, altsetting); + if (if_desc == NULL) { + r = LIBUSB_ERROR_NOT_FOUND; + goto end; + } + + safe_free(priv->usb_interface[iface].endpoint); + + if (if_desc->bNumEndpoints == 0) { + 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) { + r = LIBUSB_ERROR_NO_MEM; + goto end; + } + 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); + } + } + + // Extra init may be required to configure endpoints + if (priv->apib->configure_endpoints) + r = priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface); + + if (r == LIBUSB_SUCCESS) + priv->usb_interface[iface].current_altsetting = altsetting; + +end: + libusb_free_config_descriptor(conf_desc); + return r; +} + +// Lookup for a match in the list of API driver names +// return -1 if not found, driver match number otherwise +static int get_sub_api(char *driver, int api) +{ + const char sep_str[2] = {LIST_SEPARATOR, 0}; + char *tok, *tmp_str; + size_t len = strlen(driver); + int i; + + if (len == 0) + return SUB_API_NOTSET; + + tmp_str = _strdup(driver); + if (tmp_str == NULL) + return SUB_API_NOTSET; + + tok = strtok(tmp_str, sep_str); + while (tok != NULL) { + for (i = 0; i < usb_api_backend[api].nb_driver_names; i++) { + if (_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) { + free(tmp_str); + return i; + } + } + tok = strtok(NULL, sep_str); + } + + free(tmp_str); + return SUB_API_NOTSET; +} + +/* + * auto-claiming and auto-release helper functions + */ +static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type) +{ + 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 = *interface_number; + int r = LIBUSB_SUCCESS; + + switch (api_type) { + case USB_API_WINUSBX: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_mutex_lock(&autoclaim_lock); + if (current_interface < 0) { // No serviceable interface was found + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + // 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(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]++; + break; + } + } + if (current_interface == USB_MAXINTERFACES) { + usbi_err(TRANSFER_CTX(transfer), "could not auto-claim any interface"); + r = LIBUSB_ERROR_NOT_FOUND; + } + } else { + // If we have a valid interface that was autoclaimed, we must increment + // its autoclaim count so that we can prevent an early release. + if (handle_priv->autoclaim_count[current_interface] != 0) + handle_priv->autoclaim_count[current_interface]++; + } + usbi_mutex_unlock(&autoclaim_lock); + + *interface_number = current_interface; + return r; +} + +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 = get_winusb_device_handle_priv(dev_handle); + int r; + + usbi_mutex_lock(&autoclaim_lock); + if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) { + handle_priv->autoclaim_count[transfer_priv->interface_number]--; + 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(ITRANSFER_CTX(itransfer), "auto-released interface %d", transfer_priv->interface_number); + else + usbi_dbg(ITRANSFER_CTX(itransfer), "failed to auto-release interface %d (%s)", + transfer_priv->interface_number, libusb_error_name((enum libusb_error)r)); + } + } + usbi_mutex_unlock(&autoclaim_lock); +} + +/* + * init: libusb backend init function + */ +static int winusb_init(struct libusb_context *ctx) +{ + int i; + + // Load DLL imports + if (!init_dlls(ctx)) { + usbi_err(ctx, "could not resolve DLL functions"); + return LIBUSB_ERROR_OTHER; + } + + // Initialize the low level APIs (we don't care about errors at this stage) + for (i = 0; i < USB_API_MAX; i++) { + if (usb_api_backend[i].init && !usb_api_backend[i].init(ctx)) + usbi_warn(ctx, "error initializing %s backend", + usb_api_backend[i].designation); + } + + // We need a lock for proper auto-release + usbi_mutex_init(&autoclaim_lock); + + return LIBUSB_SUCCESS; +} + +/* +* exit: libusb backend deinitialization function +*/ +static void winusb_exit(struct libusb_context *ctx) +{ + int i; + + UNUSED(ctx); + + usbi_mutex_destroy(&autoclaim_lock); + + for (i = 0; i < USB_API_MAX; i++) { + if (usb_api_backend[i].exit) + usb_api_backend[i].exit(); + } + + exit_dlls(); +} + +/* + * fetch and cache all the config descriptors through I/O + */ +static void cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + DWORD size, ret_size; + uint8_t i, num_configurations; + + USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request + PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request + PUSB_CONFIGURATION_DESCRIPTOR cd_data; + + num_configurations = dev->device_descriptor.bNumConfigurations; + if (num_configurations == 0) + return; + + assert(sizeof(USB_DESCRIPTOR_REQUEST) == USB_DESCRIPTOR_REQUEST_SIZE); + + priv->config_descriptor = calloc(num_configurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + if (priv->config_descriptor == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor array for '%s'", priv->dev_id); + return; + } + + for (i = 0; i <= num_configurations; i++) { + safe_free(cd_buf_actual); + + if (i == num_configurations) + break; + + size = sizeof(cd_buf_short); + memset(&cd_buf_short.desc, 0, sizeof(cd_buf_short.desc)); + + cd_buf_short.req.ConnectionIndex = (ULONG)dev->port_number; + cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_short.req.SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR; + cd_buf_short.req.SetupPacket.wValue = (LIBUSB_DT_CONFIG << 8) | i; + cd_buf_short.req.SetupPacket.wIndex = 0; + cd_buf_short.req.SetupPacket.wLength = (USHORT)sizeof(USB_CONFIGURATION_DESCRIPTOR); + + // Dummy call to get the required data size. Initial failures are reported as info rather + // than error as they can occur for non-penalizing situations, such as with some hubs. + // coverity[tainted_data_argument] + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, + &cd_buf_short, size, &ret_size, NULL)) { + usbi_info(ctx, "could not access configuration descriptor %u (dummy) for '%s': %s", i, priv->dev_id, windows_error_str(0)); + continue; + } + + if ((ret_size != size) || (cd_buf_short.desc.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { + usbi_info(ctx, "unexpected configuration descriptor %u size (dummy) for '%s'", i, priv->dev_id); + continue; + } + + size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.desc.wTotalLength; + cd_buf_actual = malloc(size); + if (cd_buf_actual == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor %u buffer for '%s'", i, priv->dev_id); + continue; + } + + // Actual call + cd_buf_actual->ConnectionIndex = (ULONG)dev->port_number; + cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_actual->SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR; + cd_buf_actual->SetupPacket.wValue = (LIBUSB_DT_CONFIG << 8) | i; + cd_buf_actual->SetupPacket.wIndex = 0; + cd_buf_actual->SetupPacket.wLength = cd_buf_short.desc.wTotalLength; + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, + cd_buf_actual, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor %u (actual) for '%s': %s", i, priv->dev_id, windows_error_str(0)); + continue; + } + + cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR *)cd_buf_actual + USB_DESCRIPTOR_REQUEST_SIZE); + + if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.desc.wTotalLength)) { + usbi_err(ctx, "unexpected configuration descriptor %u size (actual) for '%s'", i, priv->dev_id); + continue; + } + + if (cd_data->bDescriptorType != LIBUSB_DT_CONFIG) { + usbi_err(ctx, "descriptor %u not a configuration descriptor for '%s'", i, priv->dev_id); + continue; + } + + usbi_dbg(ctx, "cached config descriptor %u (bConfigurationValue=%u, %u bytes)", + i, cd_data->bConfigurationValue, cd_data->wTotalLength); + + // Cache the descriptor + priv->config_descriptor[i] = cd_data; + cd_buf_actual = NULL; + } +} + +#define ROOT_HUB_FS_CONFIG_DESC_LENGTH 0x19 +#define ROOT_HUB_HS_CONFIG_DESC_LENGTH 0x19 +#define ROOT_HUB_SS_CONFIG_DESC_LENGTH 0x1f +#define CONFIG_DESC_WTOTAL_LENGTH_OFFSET 0x02 +#define CONFIG_DESC_EP_MAX_PACKET_OFFSET 0x16 +#define CONFIG_DESC_EP_BINTERVAL_OFFSET 0x18 + +static const uint8_t root_hub_config_descriptor_template[] = { + // Configuration Descriptor + LIBUSB_DT_CONFIG_SIZE, // bLength + LIBUSB_DT_CONFIG, // bDescriptorType + 0x00, 0x00, // wTotalLength (filled in) + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0xc0, // bmAttributes (reserved + self-powered) + 0x00, // bMaxPower + // Interface Descriptor + LIBUSB_DT_INTERFACE_SIZE, // bLength + LIBUSB_DT_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints + LIBUSB_CLASS_HUB, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + // Endpoint Descriptor + LIBUSB_DT_ENDPOINT_SIZE, // bLength + LIBUSB_DT_ENDPOINT, // bDescriptorType + 0x81, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + 0x00, 0x00, // wMaxPacketSize (filled in) + 0x00, // bInterval (filled in) + // SuperSpeed Endpoint Companion Descriptor + LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE, // bLength + LIBUSB_DT_SS_ENDPOINT_COMPANION, // bDescriptorType + 0x00, // bMaxBurst + 0x00, // bmAttributes + 0x02, 0x00 // wBytesPerInterval +}; + +static int alloc_root_hub_config_desc(struct libusb_device *dev, ULONG num_ports, + uint8_t config_desc_length, uint8_t ep_interval) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + uint8_t *ptr; + + priv->config_descriptor = malloc(sizeof(*priv->config_descriptor)); + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NO_MEM; + + // Most config descriptors come from cache_config_descriptors() which obtains the + // descriptors from the hub using an allocated USB_DESCRIPTOR_REQUEST structure. + // To avoid an extra malloc + memcpy we just hold on to the USB_DESCRIPTOR_REQUEST + // structure we already have and back up the pointer in windows_device_priv_release() + // when freeing the descriptors. To keep a single execution path, we need to offset + // the pointer here by the same amount. + ptr = malloc(USB_DESCRIPTOR_REQUEST_SIZE + config_desc_length); + if (ptr == NULL) + return LIBUSB_ERROR_NO_MEM; + + ptr += USB_DESCRIPTOR_REQUEST_SIZE; + + memcpy(ptr, root_hub_config_descriptor_template, config_desc_length); + ptr[CONFIG_DESC_WTOTAL_LENGTH_OFFSET] = config_desc_length; + ptr[CONFIG_DESC_EP_MAX_PACKET_OFFSET] = (uint8_t)((num_ports + 7) / 8); + ptr[CONFIG_DESC_EP_BINTERVAL_OFFSET] = ep_interval; + + priv->config_descriptor[0] = (PUSB_CONFIGURATION_DESCRIPTOR)ptr; + priv->active_config = 1; + + return 0; +} + +static int init_root_hub(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + USB_NODE_CONNECTION_INFORMATION_EX conn_info; + USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; + USB_NODE_INFORMATION hub_info; + enum libusb_speed speed = LIBUSB_SPEED_UNKNOWN; + uint8_t config_desc_length; + uint8_t ep_interval; + HANDLE handle; + ULONG port_number, num_ports; + DWORD size; + int r; + + // Determining the speed of a root hub is painful. Microsoft does not directly report the speed + // capabilities of the root hub itself, only its ports and/or connected devices. Therefore we + // are forced to query each individual port of the root hub to try and infer the root hub's + // speed. Note that we have to query all ports because the presence of a device on that port + // changes if/how Windows returns any useful speed information. + handle = CreateFileA(priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open root hub %s: %s", priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, NULL, 0, &hub_info, sizeof(hub_info), &size, NULL)) { + usbi_warn(ctx, "could not get root hub info for '%s': %s", priv->dev_id, windows_error_str(0)); + CloseHandle(handle); + return LIBUSB_ERROR_ACCESS; + } + + num_ports = hub_info.u.HubInformation.HubDescriptor.bNumberOfPorts; + 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, + // but it is not perfect. If no device is attached to the port being queried, the + // returned information will only indicate whether that port supports USB 3.0 signalling. + // That is not enough information to distinguish between SuperSpeed and SuperSpeed Plus. + for (port_number = 1; port_number <= num_ports; port_number++) { + conn_info_v2.ConnectionIndex = port_number; + conn_info_v2.Length = sizeof(conn_info_v2); + conn_info_v2.SupportedUsbProtocols.Usb300 = 1; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, + &conn_info_v2, sizeof(conn_info_v2), &conn_info_v2, sizeof(conn_info_v2), &size, NULL)) { + usbi_warn(ctx, "could not get node connection information (V2) for root hub '%s' port %lu: %s", + priv->dev_id, ULONG_CAST(port_number), windows_error_str(0)); + break; + } + + if (conn_info_v2.Flags.DeviceIsSuperSpeedPlusCapableOrHigher) + speed = MAX(speed, LIBUSB_SPEED_SUPER_PLUS); + else if (conn_info_v2.Flags.DeviceIsSuperSpeedCapableOrHigher || conn_info_v2.SupportedUsbProtocols.Usb300) + speed = MAX(speed, LIBUSB_SPEED_SUPER); + else if (conn_info_v2.SupportedUsbProtocols.Usb200) + speed = MAX(speed, LIBUSB_SPEED_HIGH); + else + speed = MAX(speed, LIBUSB_SPEED_FULL); + } + + if (speed != LIBUSB_SPEED_UNKNOWN) + goto make_descriptors; + } + + // At this point the speed is still not known, most likely because we are executing on + // Windows 7 or earlier. The following hackery peeks into the root hub's Device ID and + // tries to extract speed information from it, based on observed naming conventions. + // If this does not work, we will query individual ports of the root hub. + if (strstr(priv->dev_id, "ROOT_HUB31") != NULL) + speed = LIBUSB_SPEED_SUPER_PLUS; + else if (strstr(priv->dev_id, "ROOT_HUB30") != NULL) + speed = LIBUSB_SPEED_SUPER; + else if (strstr(priv->dev_id, "ROOT_HUB20") != NULL) + speed = LIBUSB_SPEED_HIGH; + + if (speed != LIBUSB_SPEED_UNKNOWN) + goto make_descriptors; + + // Windows only reports speed information about a connected device. This means that a root + // hub with no connected devices or devices that are all operating at a speed less than the + // highest speed that the root hub supports will not give us the correct speed. + for (port_number = 1; port_number <= num_ports; port_number++) { + conn_info.ConnectionIndex = port_number; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info), + &conn_info, sizeof(conn_info), &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for root hub '%s' port %lu: %s", + priv->dev_id, ULONG_CAST(port_number), windows_error_str(0)); + continue; + } + + if (conn_info.ConnectionStatus != DeviceConnected) + continue; + + if (conn_info.Speed == UsbHighSpeed) { + speed = LIBUSB_SPEED_HIGH; + break; + } + } + +make_descriptors: + CloseHandle(handle); + + dev->device_descriptor.bLength = LIBUSB_DT_DEVICE_SIZE; + dev->device_descriptor.bDescriptorType = LIBUSB_DT_DEVICE; + dev->device_descriptor.bDeviceClass = LIBUSB_CLASS_HUB; + if ((dev->device_descriptor.idVendor == 0) && (dev->device_descriptor.idProduct == 0)) { + dev->device_descriptor.idVendor = 0x1d6b; // Linux Foundation + dev->device_descriptor.idProduct = (uint16_t)speed; + } + dev->device_descriptor.bcdDevice = 0x0100; + dev->device_descriptor.bNumConfigurations = 1; + + switch (speed) { + case LIBUSB_SPEED_SUPER_PLUS: + dev->device_descriptor.bcdUSB = 0x0310; + config_desc_length = ROOT_HUB_SS_CONFIG_DESC_LENGTH; + ep_interval = 0x0c; // 256ms + break; + case LIBUSB_SPEED_SUPER: + dev->device_descriptor.bcdUSB = 0x0300; + config_desc_length = ROOT_HUB_SS_CONFIG_DESC_LENGTH; + ep_interval = 0x0c; // 256ms + break; + case LIBUSB_SPEED_HIGH: + dev->device_descriptor.bcdUSB = 0x0200; + config_desc_length = ROOT_HUB_HS_CONFIG_DESC_LENGTH; + ep_interval = 0x0c; // 256ms + break; + case LIBUSB_SPEED_LOW: // Not used, but keeps compiler happy + case LIBUSB_SPEED_UNKNOWN: + // This case means absolutely no information about this root hub was determined. + // There is not much choice than to be pessimistic and label this as a + // full-speed device. + speed = LIBUSB_SPEED_FULL; + // fallthrough + case LIBUSB_SPEED_FULL: + dev->device_descriptor.bcdUSB = 0x0110; + config_desc_length = ROOT_HUB_FS_CONFIG_DESC_LENGTH; + ep_interval = 0xff; // 255ms + break; + default: // Impossible, buts keeps compiler happy + usbi_err(ctx, "program assertion failed - unknown root hub speed"); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (speed >= LIBUSB_SPEED_SUPER) { + dev->device_descriptor.bDeviceProtocol = 0x03; // USB 3.0 Hub + dev->device_descriptor.bMaxPacketSize0 = 0x09; // 2^9 bytes + } else { + dev->device_descriptor.bMaxPacketSize0 = 0x40; // 64 bytes + } + + dev->speed = speed; + + r = alloc_root_hub_config_desc(dev, num_ports, config_desc_length, ep_interval); + if (r) + usbi_err(ctx, "could not allocate config descriptor for root hub '%s'", priv->dev_id); + + return r; +} + +/* + * Populate a libusb device structure + */ +static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev, + uint8_t port_number, DEVINST devinst) +{ + 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; + USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; + HANDLE hub_handle; + DWORD size; + uint8_t bus_number, depth; + int r; + + priv = usbi_get_device_priv(dev); + + // If the device is already initialized, we can stop here + if (priv->initialized) + return LIBUSB_SUCCESS; + + if (parent_dev != NULL) { // Not a HCD root hub + ctx = DEVICE_CTX(dev); + parent_priv = usbi_get_device_priv(parent_dev); + if (parent_priv->apib->id != USB_API_HUB) { + usbi_warn(ctx, "parent for device '%s' is not a hub", priv->dev_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + // Calculate depth and fetch bus number + bus_number = parent_dev->bus_number; + if (bus_number == 0) { + tmp_dev = get_ancestor(ctx, devinst, &devinst); + if (tmp_dev != parent_dev) { + usbi_err(ctx, "program assertion failed - first ancestor is not parent"); + return LIBUSB_ERROR_NOT_FOUND; + } + libusb_unref_device(tmp_dev); + + for (depth = 1; bus_number == 0; depth++) { + tmp_dev = get_ancestor(ctx, devinst, &devinst); + if (tmp_dev == NULL) { + usbi_warn(ctx, "ancestor for device '%s' not found at depth %u", priv->dev_id, depth); + return LIBUSB_ERROR_NO_DEVICE; + } + if (tmp_dev->bus_number != 0) { + bus_number = tmp_dev->bus_number; + tmp_priv = usbi_get_device_priv(tmp_dev); + depth += tmp_priv->depth; + } + libusb_unref_device(tmp_dev); + } + } else { + depth = parent_priv->depth + 1; + } + + if (bus_number == 0) { + usbi_err(ctx, "program assertion failed - bus number not found for '%s'", priv->dev_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + dev->bus_number = bus_number; + dev->port_number = port_number; + dev->parent_dev = parent_dev; + priv->depth = depth; + + hub_handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hub_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + + conn_info.ConnectionIndex = (ULONG)port_number; + // coverity[tainted_data_argument] + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info), + &conn_info, sizeof(conn_info), &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + priv->dev_id, windows_error_str(0)); + CloseHandle(hub_handle); + return LIBUSB_ERROR_NO_DEVICE; + } + + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_NO_DEVICE; + } + + if ((conn_info.DeviceDescriptor.bLength != LIBUSB_DT_DEVICE_SIZE) + || (conn_info.DeviceDescriptor.bDescriptorType != LIBUSB_DT_DEVICE)) { + usbi_warn(ctx, "device '%s' has invalid descriptor!", priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_OTHER; + } + + static_assert(sizeof(dev->device_descriptor) == sizeof(conn_info.DeviceDescriptor), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, &conn_info.DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + if (conn_info.CurrentConfigurationValue == 0) { + usbi_dbg(ctx, "found %u configurations for device '%s' but device is not configured (i.e. current config: 0), ignoring it", + dev->device_descriptor.bNumConfigurations, + priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_OTHER; + } + + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg(ctx, "found %u configurations (current config: %u) for device '%s'", + dev->device_descriptor.bNumConfigurations, priv->active_config, priv->dev_id); + + // Cache as many config descriptors as we can + cache_config_descriptors(dev, hub_handle); + + // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8 + if (windows_version >= WINDOWS_8) { + conn_info_v2.ConnectionIndex = (ULONG)port_number; + conn_info_v2.Length = sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2); + conn_info_v2.SupportedUsbProtocols.Usb300 = 1; + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, + &conn_info_v2, sizeof(conn_info_v2), &conn_info_v2, sizeof(conn_info_v2), &size, NULL)) { + usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s", + priv->dev_id, windows_error_str(0)); + } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher) { + conn_info.Speed = UsbSuperSpeedPlus; + } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) { + conn_info.Speed = UsbSuperSpeed; + } + } + + CloseHandle(hub_handle); + + if (conn_info.DeviceAddress > UINT8_MAX) + usbi_err(ctx, "program assertion failed - device address overflow"); + + dev->device_address = (uint8_t)conn_info.DeviceAddress; + + switch (conn_info.Speed) { + case UsbLowSpeed: dev->speed = LIBUSB_SPEED_LOW; break; + case UsbFullSpeed: dev->speed = LIBUSB_SPEED_FULL; break; + case UsbHighSpeed: dev->speed = LIBUSB_SPEED_HIGH; break; + case UsbSuperSpeed: dev->speed = LIBUSB_SPEED_SUPER; break; + case UsbSuperSpeedPlus: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break; + default: + usbi_warn(ctx, "unknown device speed %u", conn_info.Speed); + break; + } + } else { + r = init_root_hub(dev); + if (r) + return r; + } + + r = usbi_sanitize_device(dev); + if (r) + return r; + + priv->initialized = true; + + 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; + const char *start = NULL; + char *end = NULL; + long long port; + + // 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) { + start = buffer + 6; + // Note that 0 is both strtoll's sentinel return value to indicate failure, as well + // as (obviously) the return value for the literal "0". Fortunately we can always treat + // 0 as a failure, since Windows USB port numbers are numbered 1..n. + port = strtoll(start, &end, 10); + if (port <= 0 || port >= ULONG_MAX || end == start || (*end != '.' && *end != '\0')) { + return false; + } + *port_nr = (DWORD)port; + 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) { + start = token + 5; + port = strtoll(start, &end, 10); + if (port <= 0 || port >= ULONG_MAX || end == start || (*end != ')' && *end != '\0')) { + return false; + } + *port_nr = (DWORD)port; + 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, + DEVINST devinst) +{ + DEVINST child_devinst; + struct libusb_device* dev; + + if (CM_Get_Child(&child_devinst, devinst, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not get child devinst for '%s'", dev_id); + return LIBUSB_SUCCESS; + } + + dev = usbi_get_device_by_session_id(ctx, (unsigned long)child_devinst); + if (dev == NULL) { + usbi_warn(ctx, "HCD '%s' child not found", dev_id); + return LIBUSB_SUCCESS; + } + + if (sscanf(dev_id, "PCI\\VEN_%04hx&DEV_%04hx%*s", &dev->device_descriptor.idVendor, &dev->device_descriptor.idProduct) != 2) + usbi_warn(ctx, "could not infer VID/PID of HCD from '%s'", dev_id); + libusb_unref_device(dev); + return LIBUSB_SUCCESS; +} + +// Returns the api type, or 0 if not found/unsupported +static void get_api_type(HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, + int *api, int *sub_api) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + DWORD size, reg_type; + unsigned k, l; + int i, j; + + // Check the service & filter names to know the API we should use + for (k = 0; k < 3; k++) { + if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop, + ®_type, (PBYTE)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) + // our buffers are MAX_KEY_LENGTH + 1 so we can overflow if needed + lookup[k].list[strlen(lookup[k].list) + 1] = 0; + + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsupported one does, it's not an issue + for (l = 0; (lookup[k].list[l] != 0) || (lookup[k].list[l + 1] != 0); l++) { + if (lookup[k].list[l] == 0) + lookup[k].list[l] = LIST_SEPARATOR; + } + usbi_dbg(NULL, "%s(s): %s", lookup[k].designation, lookup[k].list); + } else { + if (GetLastError() != ERROR_INVALID_DATA) + usbi_dbg(NULL, "could not access %s: %s", lookup[k].designation, windows_error_str(0)); + lookup[k].list[0] = 0; + } + } + + for (i = 2; i < USB_API_MAX; i++) { + for (k = 0; k < 3; k++) { + j = get_sub_api(lookup[k].list, i); + if (j >= 0) { + 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; + return; + } + } + } +} + +static int set_composite_interface(struct libusb_context *ctx, struct libusb_device *dev, + char *dev_interface_path, char *device_id, int api, int sub_api) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + int interface_number; + const char *mi_str; + int iadi, iadintfi; + char* endptr; + struct libusb_interface_association_descriptor_array *iad_array; + const struct libusb_interface_association_descriptor *iad; + int r = LIBUSB_SUCCESS; + + // Because MI_## are not necessarily in sequential order (some composite + // devices will have only MI_00 & MI_03 for instance), we retrieve the actual + // interface number from the path's MI value + mi_str = strstr(device_id, "MI_"); + + endptr = NULL; + // This initialization, while redundant, is needed to make MSVC happy + interface_number = -1; + + if (mi_str != NULL) { + interface_number = strtoul(&mi_str[3], &endptr, 16); + } + + if (mi_str == NULL || endptr - &mi_str[3] != 2) { + usbi_warn(ctx, "failure to read interface number for %s, using default value", device_id); + interface_number = 0; + } + + if (interface_number >= USB_MAXINTERFACES) { + usbi_warn(ctx, "interface %d too large - ignoring interface path %s", interface_number, dev_interface_path); + return LIBUSB_ERROR_ACCESS; + } + + // Serialize concurrent enumeration callers that all reach this for the + // same parent device. Without this, the check-then-modify on + // priv->usb_interface[].path causes double-free / use-after-free. + usbi_mutex_lock(&priv->interface_lock); + + 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(ctx, "interface[%d] already set - ignoring HID collection: %s", + interface_number, device_id); + r = LIBUSB_ERROR_ACCESS; + goto out; + } + // In other cases, just use the latest data + safe_free(priv->usb_interface[interface_number].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; + if ((api == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) { + r = LIBUSB_ERROR_NO_MEM; + goto out; + } + } + + // For WinUSBX, set up associations for interfaces grouped by an IAD + if ((api == USB_API_WINUSBX) && !libusb_get_active_interface_association_descriptors(dev, &iad_array)) { + for (iadi = 0; iadi < iad_array->length; iadi++) { + iad = &iad_array->iad[iadi]; + if (iad->bFirstInterface == interface_number) { + priv->usb_interface[interface_number].num_associated_interfaces = iad->bInterfaceCount; + priv->usb_interface[interface_number].first_associated_interface = iad->bFirstInterface; + for (iadintfi = 1; iadintfi < iad->bInterfaceCount; iadintfi++) { + usbi_dbg(ctx, "interface[%d] is associated with interface[%d]", + interface_number + iadintfi, interface_number); + priv->usb_interface[interface_number + iadintfi].apib = &usb_api_backend[api]; + priv->usb_interface[interface_number + iadintfi].sub_api = sub_api; + priv->usb_interface[interface_number + iadintfi].num_associated_interfaces = iad->bInterfaceCount; + priv->usb_interface[interface_number + iadintfi].first_associated_interface = iad->bFirstInterface; + } + break; + } + } + libusb_free_interface_association_descriptors(iad_array); + } + +out: + usbi_mutex_unlock(&priv->interface_lock); + return r; +} + +static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *dev, + char *dev_interface_path) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + uint8_t i; + int r = LIBUSB_SUCCESS; + + // Serialize concurrent enumeration callers that all reach this for the + // same parent device. Protects priv->hid->nb_interfaces and + // priv->usb_interface[].path / apib. + usbi_mutex_lock(&priv->interface_lock); + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - parent is not HID"); + r = LIBUSB_ERROR_NO_DEVICE; + goto out; + } else if (priv->hid->nb_interfaces == USB_MAXINTERFACES) { + usbi_err(ctx, "program assertion failed - max USB interfaces reached for HID device"); + r = LIBUSB_ERROR_NO_DEVICE; + goto out; + } + + 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(ctx, "interface[%u] already set to %s", i, dev_interface_path); + r = LIBUSB_ERROR_ACCESS; + goto out; + } + } + + 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(ctx, "interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path); + priv->hid->nb_interfaces++; + +out: + usbi_mutex_unlock(&priv->interface_lock); + return r; +} + +// get the n-th device interface GUID indexed by guid_number +static int get_guid(struct libusb_context *ctx, char *dev_id, HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, + int guid_number, GUID **if_guid) +{ + DWORD size, reg_type; + HKEY key; + char *guid_string, *new_guid_string; + char *guid, *guid_term; + LONG s; + int pass, guids_left; + int err = LIBUSB_SUCCESS; +#if !defined(ENABLE_LOGGING) + UNUSED(dev_id); +#endif + + key = pSetupDiOpenDevRegKey(*dev_info, dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "Cannot get the additional GUIDs for '%s'", dev_id); + return LIBUSB_ERROR_ACCESS; + } + // Reserve buffer large enough to hold one GUID with two terminating characters + size = MAX_GUID_STRING_LENGTH + 1; + // Allocate memory for storing the guid_string with two extra terminating characters + // This is necessary for parsing the REG_MULTI_SZ type below + guid_string = malloc(size + 2); + if (guid_string == NULL) { + usbi_err(ctx, "failed to alloc guid_string"); + return LIBUSB_ERROR_NO_MEM; + } + + // The 1st pass tries to get the guid. If it fails due to ERROR_MORE_DATA + // then reallocate enough memory for the 2nd pass + for (pass = 0; pass < 2; pass++) { + // Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order + // If multiple GUIDs, find the n-th that is indexed by guid_number + s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, ®_type, + (LPBYTE)guid_string, &size); + if (s == ERROR_FILE_NOT_FOUND) + s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_type, + (LPBYTE)guid_string, &size); + if (s == ERROR_SUCCESS) { + // The GUID was read successfully + break; + } else if (s == ERROR_FILE_NOT_FOUND) { + usbi_dbg(ctx, "no DeviceInterfaceGUID registered for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } else if (s == ERROR_MORE_DATA) { + if (pass == 1) { + // Previous pass should have allocated enough memory, but reading failed + usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id); + err = LIBUSB_ERROR_OTHER; + goto exit; + } + new_guid_string = realloc((void *)guid_string, size + 2); + if (new_guid_string == NULL) { + usbi_err(ctx, "failed to realloc guid string"); + err = LIBUSB_ERROR_NO_MEM; + goto exit; + } + guid_string = new_guid_string; + } else { + usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + } + + // 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" + // - The following GUIDs should be consider as valid: + // "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\0", "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", + // "{xxx.....xx}\0\0\0", "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0", + // "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}", "{xxx.....xx}{xxx.....xx}{xxx.....xx}", + // "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0\0\0\0" + if ((reg_type == REG_SZ ) || (reg_type == REG_MULTI_SZ)) { + /* Get the n-th GUID indexed by guid_number since the DeviceInterfaceGUIDs may + contain more GUIDs */ + guid = guid_string; + // Add two terminating chars for not overrunning the allocated memory while iterating + guid[size] = '\0'; + guid[size + 1] = '\0'; + // Iterate the GUIDs in the guid string + guids_left = guid_number; + while (guids_left) { + guid = strchr(guid, '}'); + if (guid == NULL) { + usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + guid++; + // Skip the terminating char if available + if (*guid == '\0') { + guid++; + } + guids_left--; + } + // Add terminating char to the string + guid_term = strchr(guid, '}'); + if (guid_term == NULL) { + usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + // Terminate the current guid string to handle the variant without separators + guid_term++; + *guid_term = '\0'; + } else { + usbi_warn(ctx, "unexpected type of DeviceInterfaceGUID for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + + *if_guid = malloc(sizeof(GUID)); + if (*if_guid == NULL) { + usbi_err(ctx, "failed to alloc if_guid"); + err = LIBUSB_ERROR_NO_MEM; + goto exit; + } + if (!string_to_guid(guid, *if_guid)) { + usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid); + free(*if_guid); + *if_guid = NULL; + goto exit; + } + +exit: + pRegCloseKey(key); + free(guid_string); + return err; +} + +/* + * get_device_list: libusb backend device enumeration function + */ +static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + struct discovered_devs *discdevs; + HDEVINFO *dev_info, dev_info_intf, dev_info_enum; + SP_DEVINFO_DATA dev_info_data; + DWORD _index = 0; + GUID hid_guid; + int r = LIBUSB_SUCCESS; + int api, sub_api; + unsigned int pass, pass_type, i, j; + char enumerator[16]; + char dev_id[MAX_PATH_LENGTH]; + struct libusb_device *dev, *parent_dev; + struct winusb_device_priv *priv, *parent_priv; + char *dev_interface_path = NULL; + unsigned long session_id; + DWORD size, port_nr, install_state; + uint8_t bus_number = 0; +#if defined(ENABLE_LOGGING) + char guid_string[MAX_GUID_STRING_LENGTH]; +#endif + GUID *if_guid; +#define HUB_PASS 0 +#define DEV_PASS 1 +#define HCD_PASS 2 +#define GEN_PASS 3 +#define HID_PASS 4 +#define EXT_PASS 5 + // Keep a list of guids that will be enumerated +#define GUID_SIZE_STEP 8 + const GUID **guid_list, **new_guid_list; + unsigned int guid_size = GUID_SIZE_STEP; + unsigned int nb_guids; + // Keep a list of PnP enumerator strings that are found + const char *usb_enumerator[8] = { "USB" }; + unsigned int nb_usb_enumerators = 1; + unsigned int usb_enum_index = 0; + // Keep a list of newly allocated devs to unref +#define UNREF_SIZE_STEP 16 + libusb_device **unref_list, **new_unref_list; + unsigned int unref_size = UNREF_SIZE_STEP; + unsigned int unref_cur = 0; + DWORD hub_port_nr; + + // PASS 0 : enumerate HUBs + // PASS 1 : (re)enumerate master devices that have a DEVice interface + // PASS 2 : (re)enumerate HCDs (allow for HCD hotplug) + // PASS 3 : (re)enumerate GENeric devices (including driverless) + // and list additional device interface GUIDs to explore + // PASS 4 : (re)enumerate device interface GUIDs (including HID) + // and set the device interfaces + // PASS 5+: (re)enumerate additional EXTra GUID devices + + // Init the GUID table + guid_list = malloc(guid_size * sizeof(void *)); + if (guid_list == NULL) { + usbi_err(ctx, "failed to alloc guid list"); + return LIBUSB_ERROR_NO_MEM; + } + + guid_list[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; + guid_list[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; + guid_list[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + guid_list[GEN_PASS] = NULL; + if (HidD_GetHidGuid != NULL) { + HidD_GetHidGuid(&hid_guid); + guid_list[HID_PASS] = &hid_guid; + } else { + guid_list[HID_PASS] = NULL; + } + nb_guids = EXT_PASS; + + unref_list = malloc(unref_size * sizeof(void *)); + if (unref_list == NULL) { + usbi_err(ctx, "failed to alloc unref list"); + free((void *)guid_list); + return LIBUSB_ERROR_NO_MEM; + } + + dev_info_intf = pSetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dev_info_intf == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "failed to obtain device info list: %s", windows_error_str(0)); + free(unref_list); + free((void *)guid_list); + return LIBUSB_ERROR_OTHER; + } + + for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { + pass_type = MIN(pass, EXT_PASS); +#if defined(ENABLE_LOGGING) + const char * const passname[] = {"HUB", "DEV", "HCD", "GEN", "HID", "EXT"}; + usbi_dbg(ctx, "ENUM pass %s %s", passname[pass_type], guid_to_string(guid_list[pass], guid_string)); +#endif + if ((pass == HID_PASS) && (guid_list[HID_PASS] == NULL)) + continue; + + dev_info = (pass != GEN_PASS) ? &dev_info_intf : &dev_info_enum; + + for (i = 0; ; i++) { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_path); + priv = parent_priv = NULL; + dev = parent_dev = NULL; + + // Safe loop: end of loop conditions + if (r != LIBUSB_SUCCESS) + break; + + if (pass != GEN_PASS) { + // Except for GEN, all passes deal with device interfaces + r = get_interface_details(ctx, *dev_info, &dev_info_data, guid_list[pass], &_index, &dev_interface_path); + if ((r != LIBUSB_SUCCESS) || (dev_interface_path == NULL)) { + _index = 0; + break; + } + } else { + // Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are + // being listed under the "NUSB3" PnP Symbolic Name rather than "USB". + // The Intel USB 3.0 driver behaves similar, but uses "IUSB3" + // The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3" + for (; usb_enum_index < nb_usb_enumerators; usb_enum_index++) { + if (get_devinfo_data(ctx, dev_info, &dev_info_data, usb_enumerator[usb_enum_index], i)) + break; + i = 0; + } + if (usb_enum_index == nb_usb_enumerators) + break; + } + + // Read the Device ID path + if (!pSetupDiGetDeviceInstanceIdA(*dev_info, &dev_info_data, dev_id, sizeof(dev_id), NULL)) { + usbi_warn(ctx, "could not read the device instance ID for devInst %lX, skipping", + ULONG_CAST(dev_info_data.DevInst)); + continue; + } + + usbi_dbg(ctx, "ENUM processing %s", dev_id); + + // Set API to use or get additional data from generic pass + api = USB_API_UNSUPPORTED; + sub_api = SUB_API_NOTSET; + switch (pass_type) { + case HCD_PASS: + break; + case HUB_PASS: + api = USB_API_HUB; + // Fetch the PnP enumerator class for this hub + // This will allow us to enumerate all classes during the GEN pass + if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME, + NULL, (PBYTE)enumerator, sizeof(enumerator), NULL)) { + usbi_err(ctx, "could not read enumerator string for device '%s': %s", dev_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_OTHER); + } + for (j = 0; j < nb_usb_enumerators; j++) { + if (strcmp(usb_enumerator[j], enumerator) == 0) + break; + } + if (j == nb_usb_enumerators) { + 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) { + nb_usb_enumerators++; + } else { + usbi_err(ctx, "could not allocate enumerator string '%s'", enumerator); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + } else { + usbi_warn(ctx, "too many enumerator strings, some devices may not be accessible"); + } + } + break; + case GEN_PASS: + // We use the GEN pass to detect driverless devices... + if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_DRIVER, + NULL, NULL, 0, NULL) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { + usbi_info(ctx, "The following device has no driver: '%s'", dev_id); + usbi_info(ctx, "libusb will not be able to access it"); + } + // ...and to add the additional device interface GUIDs + r = get_guid(ctx, dev_id, dev_info, &dev_info_data, 0, &if_guid); + if (r == LIBUSB_SUCCESS && if_guid != NULL) { + // Check if we've already seen this GUID + for (j = EXT_PASS; j < nb_guids; j++) { + if (memcmp(guid_list[j], if_guid, sizeof(*if_guid)) == 0) + break; + } + if (j == nb_guids) { + usbi_dbg(ctx, "extra GUID: %s", guid_to_string(if_guid, guid_string)); + // Extend the guid_list capacity if needed + if (nb_guids == guid_size) { + new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *)); + if (new_guid_list == NULL) { + usbi_err(ctx, "failed to realloc guid list"); + free(if_guid); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + guid_list = new_guid_list; + guid_size += GUID_SIZE_STEP; + } + guid_list[nb_guids++] = if_guid; + } else { + // Duplicate, ignore + free(if_guid); + } + } else if (r == LIBUSB_ERROR_ACCESS) { + r = LIBUSB_SUCCESS; + } else if (r == LIBUSB_ERROR_NO_MEM) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } else { + if (r != LIBUSB_SUCCESS) { + usbi_warn(ctx, "unexpected error during getting DeviceInterfaceGUID for '%s'", dev_id); + } + } + break; + case HID_PASS: + api = USB_API_HID; + break; + case DEV_PASS: + case EXT_PASS: + // Get the API type (after checking that the driver installation is OK) + if ((!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + NULL, (PBYTE)&install_state, sizeof(install_state), &size)) || (size != sizeof(install_state))) { + usbi_warn(ctx, "could not detect installation state of driver for '%s': %s", + dev_id, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %lu) - skipping", + dev_id, ULONG_CAST(install_state)); + continue; + } + get_api_type(dev_info, &dev_info_data, &api, &sub_api); + break; + default: + assert(false); // unreachable since all pass types covered explicitly + } + + // Find parent device (for the passes that need it) + if (pass >= GEN_PASS) { + parent_dev = get_ancestor(ctx, dev_info_data.DevInst, NULL); + if (parent_dev == NULL) { + // Root hubs will not have a parent + dev = usbi_get_device_by_session_id(ctx, (unsigned long)dev_info_data.DevInst); + if (dev != NULL) { + priv = usbi_get_device_priv(dev); + if (priv->root_hub) + goto track_unref; + libusb_unref_device(dev); + } + + usbi_dbg(ctx, "unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id); + continue; + } + + parent_priv = usbi_get_device_priv(parent_dev); + // virtual USB devices are also listed during GEN - don't process these yet + if ((pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB)) { + libusb_unref_device(parent_dev); + continue; + } + } + + // Create new or match existing device, using the devInst as session id + if ((pass <= GEN_PASS) && (pass != HCD_PASS)) { // For subsequent passes, we'll lookup the parent + // These are the passes that create "new" devices + session_id = (unsigned long)dev_info_data.DevInst; + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + alloc_device: + 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); + // Initialize priv (including interface_lock) and the + // fields read by concurrent enumeration paths + // (dev_id, class_guid) BEFORE making the device visible + // via usbi_attach_device or usbi_connect_device. This + // closes a use-after-free in the HOTPLUG error path: + // if usbi_attach_device runs before _strdup(dev_id) and + // the strdup then fails, libusb_unref_device frees the + // device while it is still in ctx->usb_devs, leaving a + // dangling pointer for the next list traversal. It also + // removes the (theoretical, in practice unreachable in + // HOTPLUG mode) window where concurrent paths could see + // the device with priv->dev_id == NULL. + 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); + } +#if defined(LIBUSB_WINDOWS_HOTPLUG) + usbi_attach_device(dev); + priv->seen_during_scan = true; + goto dont_track_unref; +#else + // Device priv is now initialized; make it + // discoverable via usbi_get_device_by_session_id + usbi_connect_device(dev); +#endif + } else { + usbi_dbg(ctx, "found existing device for session [%lX]", session_id); + + priv = usbi_get_device_priv(dev); + + if (priv->root_hub && dev->bus_number == bus_number + 1) { + // The bus number has already been assigned + bus_number++; + } + + if (strcmp(priv->dev_id, dev_id) != 0) { + usbi_dbg(ctx, "device instance ID for session [%lX] changed", session_id); + usbi_detach_device(dev); // usbi_detach_device is equivalent do usbi_disconnect_device but for the firing of LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT + 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_detach_device(dev); // usbi_detach_device is equivalent do usbi_disconnect_device but for the firing of LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT + libusb_unref_device(dev); + goto alloc_device; + } +#if defined(LIBUSB_WINDOWS_HOTPLUG) + priv->seen_during_scan = true; +#endif + } + + track_unref: + // Keep track of devices that need unref + if (unref_cur == unref_size) { + new_unref_list = realloc(unref_list, (unref_size + UNREF_SIZE_STEP) * sizeof(void *)); + if (new_unref_list == NULL) { + usbi_err(ctx, "could not realloc list for unref - aborting"); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + unref_list = new_unref_list; + unref_size += UNREF_SIZE_STEP; + } + unref_list[unref_cur++] = dev; + } +#if defined(LIBUSB_WINDOWS_HOTPLUG) + dont_track_unref: +#endif + // Setup device + switch (pass_type) { + case HUB_PASS: + case DEV_PASS: + // Serialize concurrent enumerations so they don't both see + // priv->path == NULL and both run setup, racing on the + // writes below. + usbi_mutex_lock(&priv->interface_lock); + // If the device has already been setup, don't do it again + if (priv->path != NULL) { + usbi_mutex_unlock(&priv->interface_lock); + break; + } + // Take care of API initialization + priv->path = dev_interface_path; + dev_interface_path = NULL; + priv->apib = &usb_api_backend[api]; + priv->sub_api = sub_api; + switch (api) { + case USB_API_COMPOSITE: + break; + case USB_API_HUB: + parent_dev = get_ancestor(ctx, dev_info_data.DevInst, NULL); + if (parent_dev == NULL) { + if (!get_dev_port_number(*dev_info, &dev_info_data, &hub_port_nr) || hub_port_nr == 0) { + if (bus_number == UINT8_MAX) { + usbi_warn(ctx, "program assertion failed - found more than %u buses, skipping the rest", UINT8_MAX); + break; + } + priv->root_hub = true; + dev->bus_number = ++bus_number; + usbi_dbg(ctx, "assigning Root Hub '%s' bus number %u", dev_id, bus_number); + } + } else { + libusb_unref_device(parent_dev); + } + break; + case USB_API_HID: + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) { + usbi_mutex_unlock(&priv->interface_lock); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = _strdup(priv->path); + if (priv->usb_interface[0].path == NULL) { + usbi_mutex_unlock(&priv->interface_lock); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + // The following is needed if we want API calls to work for both simple + // and composite devices. + for (j = 0; j < USB_MAXINTERFACES; j++) + priv->usb_interface[j].apib = &usb_api_backend[api]; + break; + } + usbi_mutex_unlock(&priv->interface_lock); + break; + case HCD_PASS: + r = enumerate_hcd_root_hub(ctx, dev_id, dev_info_data.DevInst); + break; + case GEN_PASS: + port_nr = 0; + // Serialize concurrent enumerations so they don't both see + // priv->initialized == false and both run init_device, + // which allocates priv->config_descriptor[] and other + // per-device state. Without this, the loser of the race + // overwrites the winner's allocations, leaking them. + usbi_mutex_lock(&priv->interface_lock); + if (priv->initialized) { + usbi_mutex_unlock(&priv->interface_lock); + libusb_unref_device(parent_dev); + r = LIBUSB_SUCCESS; + } else { + 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); + usbi_mutex_unlock(&priv->interface_lock); + } + if (r == LIBUSB_SUCCESS) { + // Append device to the list of discovered devices + if (_discdevs) { + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + + *_discdevs = discdevs; + } + } else { + // Failed to initialize a single device doesn't stop us from enumerating all other devices, + // but we skip it (don't add to list of discovered devices) + usbi_warn(ctx, "failed to initialize device '%s'", priv->dev_id); + r = LIBUSB_SUCCESS; +#if defined(LIBUSB_WINDOWS_HOTPLUG) + usbi_detach_device(dev); + libusb_unref_device(dev); +#endif + } + break; + case HID_PASS: + case EXT_PASS: + 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(ctx, "setting HID interface for [%lX]:", parent_dev->session_data); + r = set_hid_interface(ctx, parent_dev, dev_interface_path); + } else { + 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) { + case LIBUSB_SUCCESS: + dev_interface_path = NULL; + break; + case LIBUSB_ERROR_ACCESS: + // interface has already been set => make sure dev_interface_path is freed then + r = LIBUSB_SUCCESS; + break; + default: + LOOP_BREAK(r); + break; + } + } + libusb_unref_device(parent_dev); + break; + default: + assert(false); // unreachable since all pass types covered explicitly + } + } + } + + pSetupDiDestroyDeviceInfoList(dev_info_intf); + + // Free any additional GUIDs + for (pass = EXT_PASS; pass < nb_guids; pass++) + free((void *)guid_list[pass]); + free((void *)guid_list); + + // Free any PnP enumerator strings + for (i = 1; i < nb_usb_enumerators; i++) + free((void *)usb_enumerator[i]); + + // Unref newly allocated devs + for (i = 0; i < unref_cur; i++) + libusb_unref_device(unref_list[i]); + free(unref_list); + + return r; +} + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +struct string_descriptor_s { + UCHAR bLength; + UCHAR bDescriptorType; + uint8_t bString[252]; // UTF-16LE, must be even length +} LIBUSB_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +struct string_descriptor_req_s { + USB_DESCRIPTOR_REQUEST req; + struct string_descriptor_s desc; +}; + +/* + * \brief Convert a UTF-16 little endian string to a UTF-8 string. + * + * \param src The input UTF-16 little endian string. + * \param src_length The length of the src buffer in bytes which + * may or may not include the null terminator. + * \param dst The output UTF-8 string. + * \param dst_length The length of dst buffer in bytes, which should + * be large enough to fit the string including null terminator. + * \return The total number of bytes in the UTF-8 string, including + * the null terminator. + * If this is greater than dst_length, then dst is truncated. + * \note From MiniBitty RTOS & framework, currently closed source. + */ +static int usbi_utf16le_to_utf8(uint8_t const *src, int src_length, char *dst, int dst_length) { + int count = 0; + uint32_t codepoint = 0; + uint32_t next_codepoint = 0; + bool is_surrogate_pair = false; + bool overflow = false; + + if (NULL == dst) { + dst_length = 0; + } + if (dst_length > 0) { + dst[0] = 0; // empty string by default + } + if (src_length & 1) { + --src_length; // odd length not valid, but proceed. + } + if ((src == NULL) || (src_length <= 0)) { + return 1; + } + + for (int k = 0; k < src_length; k += 2) { + next_codepoint = *src++; + next_codepoint |= ((uint16_t)*src++) << 8; + if (is_surrogate_pair) { + is_surrogate_pair = false; + if ((next_codepoint >= 0xDC00) && (next_codepoint <= 0xDFFF)) { + // correct encoding + codepoint |= (next_codepoint & 0x3ff); + } else { + // incorrect encoding, skip previous codepoint + codepoint = next_codepoint; + } + } else if ((next_codepoint >= 0xD800) && (next_codepoint <= 0xDBFF)) { + is_surrogate_pair = true; + codepoint = 0x010000 + ((next_codepoint & 0x3ff) << 10); + continue; + } else { + codepoint = next_codepoint; + } + + if (codepoint <= 0x7f) { + if (count < dst_length) { + dst[count] = (char)codepoint; + } else { + overflow = true; + } + count += 1; + if (0 == codepoint) { + --count; // add null-terminator below + break; + } + } else if (codepoint <= 0x7ff) { + if ((count + 1) < dst_length) { + dst[count] = (char)(0xC0 | (codepoint >> 6)); + dst[count + 1] = (char)(0x80 | (codepoint & 0x3F)); + } else { + overflow = true; + } + count += 2; + } else if (codepoint <= 0xffff) { + if ((count + 2) < dst_length) { + dst[count] = (char)(0xE0 | (codepoint >> 12)); + dst[count + 1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + dst[count + 2] = (char)(0x80 | (codepoint & 0x3F)); + } else { + overflow = true; + } + count += 3; + } else if (codepoint <= 0x10ffff) { + if ((count + 3) < dst_length) { + dst[count] = (char)(0xF0 | ((codepoint >> 18) & 7)); + dst[count + 1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); + dst[count + 2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + dst[count + 3] = (char)(0x80 | (codepoint & 0x3F)); + } else { + overflow = true; + } + count += 4; + } else { + // invalid codepoint, skip + } + } + + if (count < dst_length) { + dst[count] = 0; + } + ++count; // include null terminator + + if (overflow && dst_length) { + // truncate respecting UTF-8 character boundaries + int idx = dst_length - 1; + while (idx && (0x80 == (dst[idx] & 0xC0))) { // utf-8 continuation byte + --idx; + } + dst[idx] = 0; + } + + return count; +} + +/* + * Backend implementation for libusb_get_device_string(). + * + * Windows makes getting the common device strings + * very difficult. DEVPKEY_Device_* does not have SerialNumber, + * and it reports the driver manufacturer, not the device manufacturer. + * + * We could parse the serial number from the DEVICE_ID string: + * https://learn.microsoft.com/en-us/windows-hardware/drivers/install/device-instance-ids + * https://learn.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers + * + * However, using the dev_id for getting the serial number is + * definitely not recommended. + * + * The following implementation uses an IOCTL + * to the parent USB hub to perform the USB control request for the + * string descriptor without opening the USB device. + * While we would rather not invoke USB IO, we currently lack a + * better option. + */ +static int winusb_get_device_string(libusb_device *dev, + enum libusb_device_string_type string_type, char *data, int length) +{ + struct winusb_device_priv* priv = usbi_get_device_priv(dev); + struct libusb_context* ctx = DEVICE_CTX(dev); + DWORD size; + DWORD ret_size; + struct string_descriptor_req_s sd; + + if ((NULL != data) && (length > 0)) { + *data = 0; + } + if (NULL == dev->parent_dev) { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + uint8_t string_descriptor_idx; + switch (string_type) { + case LIBUSB_DEVICE_STRING_MANUFACTURER: string_descriptor_idx = dev->device_descriptor.iManufacturer; break; + case LIBUSB_DEVICE_STRING_PRODUCT: string_descriptor_idx = dev->device_descriptor.iProduct; break; + case LIBUSB_DEVICE_STRING_SERIAL_NUMBER: string_descriptor_idx = dev->device_descriptor.iSerialNumber; break; + default: return LIBUSB_ERROR_INVALID_PARAM; + } + + if (0 == string_descriptor_idx) { + return LIBUSB_ERROR_NOT_FOUND; + } + + struct winusb_device_priv* hub_priv = usbi_get_device_priv(dev->parent_dev); + HANDLE hub_handle = CreateFileA(hub_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hub_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", hub_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + + // Fetch and cache the device's primary USB language ID. + // String descriptor 0 with wIndex=0 returns the supported language table + // (USB 2.0 spec section 9.6.7). We use the first (primary) LANGID for + // all subsequent string descriptor requests. + if (0 == priv->langid) { + size = sizeof(sd); + memset(&sd, 0, size); + sd.req.ConnectionIndex = (ULONG)dev->port_number; + sd.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + sd.req.SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR; + sd.req.SetupPacket.wValue = (LIBUSB_DT_STRING << 8) | 0; + sd.req.SetupPacket.wIndex = 0; + sd.req.SetupPacket.wLength = (USHORT)sizeof(sd.desc); + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &sd, size, + &sd, size, &ret_size, NULL)) { + usbi_warn(ctx, "could not retrieve language IDs for '%s': %s", + priv->dev_id, windows_error_str(0)); + CloseHandle(hub_handle); + return LIBUSB_ERROR_IO; + } + + if (sd.desc.bDescriptorType != LIBUSB_DT_STRING || sd.desc.bLength < 4) { + usbi_warn(ctx, "invalid language ID descriptor for '%s'", priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_IO; + } + + priv->langid = (uint16_t)sd.desc.bString[0] | ((uint16_t)sd.desc.bString[1] << 8); + usbi_dbg(ctx, "cached language ID 0x%04x for '%s'", priv->langid, priv->dev_id); + } + + size = sizeof(sd); + memset(&sd, 0, size); + sd.req.ConnectionIndex = (ULONG)dev->port_number; + sd.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + sd.req.SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR; + sd.req.SetupPacket.wValue = (LIBUSB_DT_STRING << 8) | string_descriptor_idx; + sd.req.SetupPacket.wIndex = priv->langid; + sd.req.SetupPacket.wLength = (USHORT)sizeof(sd.desc); + + BOOL rv = DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &sd, size, + &sd, size, &ret_size, NULL); + CloseHandle(hub_handle); + if (!rv) { + usbi_err(ctx, "could not access string descriptor %u for '%s': %s", string_descriptor_idx, + priv->dev_id, windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + if (sd.desc.bDescriptorType != LIBUSB_DT_STRING) { + usbi_err(ctx, "descriptor %u not a string descriptor for '%s'", string_descriptor_idx, priv->dev_id); + return LIBUSB_ERROR_IO; + } + + return usbi_utf16le_to_utf8(sd.desc.bString, (int) (sd.desc.bLength - 2), data, length); +} + +static int winusb_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + + if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + config_header = priv->config_descriptor[config_index]; + + len = MIN(len, config_header->wTotalLength); + memcpy(buffer, config_header, len); + return (int)len; +} + +static int winusb_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, + void **buffer) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + uint8_t index; + + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NOT_FOUND; + + for (index = 0; index < dev->device_descriptor.bNumConfigurations; index++) { + config_header = priv->config_descriptor[index]; + if (config_header == NULL) + continue; + if (config_header->bConfigurationValue == bConfigurationValue) { + *buffer = config_header; + return (int)config_header->wTotalLength; + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +/* + * return the cached copy of the active config descriptor + */ +static int winusb_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + void *config_desc; + int r; + + if (priv->active_config == 0) + return LIBUSB_ERROR_NOT_FOUND; + + r = winusb_get_config_descriptor_by_value(dev, priv->active_config, &config_desc); + if (r < 0) + return r; + + len = MIN(len, (size_t)r); + memcpy(buffer, config_desc, len); + return (int)len; +} + +static int winusb_open(struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + CHECK_SUPPORTED_API(priv->apib, open); + + return priv->apib->open(SUB_API_NOTSET, dev_handle); +} + +static void winusb_close(struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (priv->apib->close) + priv->apib->close(SUB_API_NOTSET, dev_handle); +} + +static int winusb_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + *config = priv->active_config; + return LIBUSB_SUCCESS; +} + +/* + * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver + * does not currently expose a service that allows higher-level drivers to set + * the configuration." + */ +static int winusb_set_configuration(struct libusb_device_handle *dev_handle, uint8_t config) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int r = LIBUSB_SUCCESS; + + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_CONFIGURATION, config, + 0, NULL, 0, 1000); + + if (r == LIBUSB_SUCCESS) + priv->active_config = config; + + return r; +} + +static int winusb_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int r; + + CHECK_SUPPORTED_API(priv->apib, claim_interface); + + usbi_mutex_lock(&priv->interface_lock); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints = 0; + + r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface); + + if (r == LIBUSB_SUCCESS) + r = windows_assign_endpoints(dev_handle, iface, 0); + + usbi_mutex_unlock(&priv->interface_lock); + + return r; +} + +static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int r; + + CHECK_SUPPORTED_API(priv->apib, set_interface_altsetting); + + usbi_mutex_lock(&priv->interface_lock); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints = 0; + + r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting); + + if (r == LIBUSB_SUCCESS) + r = windows_assign_endpoints(dev_handle, iface, altsetting); + + usbi_mutex_unlock(&priv->interface_lock); + + return r; +} + +static int winusb_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int r; + + CHECK_SUPPORTED_API(priv->apib, release_interface); + + // The backend release paths (composite_release_interface, + // hid_release_interface) read priv->usb_interface[iface].apib / + // .path, which are written under interface_lock by claim/altsetting + // and by the enumeration setup paths. Take the lock to make those + // reads safe against concurrent writers. + usbi_mutex_lock(&priv->interface_lock); + r = priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface); + usbi_mutex_unlock(&priv->interface_lock); + + return r; +} + +static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + CHECK_SUPPORTED_API(priv->apib, clear_halt); + + return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint); +} + +static int winusb_reset_device(struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + CHECK_SUPPORTED_API(priv->apib, reset_device); + + return priv->apib->reset_device(SUB_API_NOTSET, dev_handle); +} + +static void winusb_destroy_device(struct libusb_device *dev) +{ + winusb_device_priv_release(dev); +} + +static void winusb_clear_transfer_priv(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); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + int sub_api = priv->sub_api; + + safe_free(transfer_priv->hid_buffer); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && sub_api == SUB_API_WINUSB) { + if (transfer_priv->isoch_buffer_handle != NULL) { + if (WinUSBX[sub_api].UnregisterIsochBuffer(transfer_priv->isoch_buffer_handle)) { + transfer_priv->isoch_buffer_handle = NULL; + } else { + usbi_warn(TRANSFER_CTX(transfer), "failed to unregister WinUSB isoch buffer: %s", windows_error_str(0)); + } + } + } + + safe_free(transfer_priv->iso_context); + + // When auto claim is in use, attempt to release the auto-claimed interface + auto_release(itransfer); +} + +static int winusb_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + int (*transfer_fn)(int, struct usbi_transfer *); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + transfer_fn = priv->apib->submit_control_transfer; + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + transfer_fn = priv->apib->submit_bulk_transfer; + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + transfer_fn = priv->apib->submit_iso_transfer; + break; + default: + // Should not get here since windows_submit_transfer() validates + // the transfer->type field + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (transfer_fn == NULL) { + usbi_warn(TRANSFER_CTX(transfer), + "unsupported transfer type %d (unrecognized device driver)", + transfer->type); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return transfer_fn(SUB_API_NOTSET, itransfer); +} + +static int winusb_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + + CHECK_SUPPORTED_API(priv->apib, cancel_transfer); + + return priv->apib->cancel_transfer(SUB_API_NOTSET, itransfer); +} + +static enum libusb_transfer_status winusb_copy_transfer_data(struct usbi_transfer *itransfer, DWORD length) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + + if (priv->apib->copy_transfer_data == NULL) { + usbi_err(TRANSFER_CTX(transfer), "program assertion failed - no function to copy transfer data"); + return LIBUSB_TRANSFER_ERROR; + } + + return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, length); +} + +static int winusb_endpoint_supports_raw_io(struct libusb_device_handle* dev_handle, uint8_t endpoint) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (priv->apib->endpoint_supports_raw_io == NULL) + { + usbi_dbg(HANDLE_CTX(dev_handle), "device driver does not support RAW_IO query -> unsupported."); + return 0; + } + + return priv->apib->endpoint_supports_raw_io(SUB_API_NOTSET, dev_handle, endpoint); +} + +static int winusb_endpoint_set_raw_io(struct libusb_device_handle* dev_handle, uint8_t endpoint, int enable) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (priv->apib->endpoint_set_raw_io == NULL) + { + usbi_err(HANDLE_CTX(dev_handle), "device driver does not support setting RAW_IO."); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return priv->apib->endpoint_set_raw_io(SUB_API_NOTSET, dev_handle, endpoint, enable); +} + +static int winusb_get_max_raw_io_transfer_size( + struct libusb_device_handle *dev_handle, + uint8_t endpoint) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + if (priv->apib->get_max_raw_io_transfer_size == NULL) + { + usbi_err(HANDLE_CTX(dev_handle), "device driver does not support RAW_IO max size query."); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return priv->apib->get_max_raw_io_transfer_size(SUB_API_NOTSET, dev_handle, endpoint); +} + +// NB: MSVC6 does not support named initializers. +const struct windows_backend winusb_backend = { + winusb_init, + winusb_exit, + winusb_get_device_list, + winusb_get_device_string, + winusb_open, + winusb_close, + winusb_get_active_config_descriptor, + winusb_get_config_descriptor, + winusb_get_config_descriptor_by_value, + winusb_get_configuration, + winusb_set_configuration, + winusb_claim_interface, + winusb_release_interface, + winusb_set_interface_altsetting, + winusb_clear_halt, + winusb_reset_device, + winusb_destroy_device, + winusb_submit_transfer, + winusb_cancel_transfer, + winusb_clear_transfer_priv, + winusb_copy_transfer_data, + winusb_endpoint_supports_raw_io, + winusb_endpoint_set_raw_io, + winusb_get_max_raw_io_transfer_size, +}; + +/* + * USB API backends + */ + +static const char * const composite_driver_names[] = { + "USBCCGP", // (Windows built-in) USB Composite Device + "dg_ssudbus" // SAMSUNG Mobile USB Composite Device +}; +static const char * const winusbx_driver_names[] = {"libusbK", "libusb0", "WinUSB"}; +static const char * const hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + USB_API_UNSUPPORTED, + "Unsupported API", + NULL, /* driver_name_list */ + 0, /* nb_driver_names */ + NULL, /* init */ + NULL, /* exit */ + NULL, /* open */ + NULL, /* close */ + NULL, /* configure_endpoints */ + NULL, /* claim_interface */ + NULL, /* set_interface_altsetting */ + NULL, /* release_interface */ + NULL, /* clear_halt */ + NULL, /* reset_device */ + NULL, /* submit_bulk_transfer */ + NULL, /* submit_iso_transfer */ + NULL, /* submit_control_transfer */ + NULL, /* cancel_transfer */ + NULL, /* copy_transfer_data */ + NULL, /* endpoint_supports_raw_io */ + NULL, /* endpoint_set_raw_io */ + NULL, /* get_max_raw_io_transfer_size */ + }, + { + USB_API_HUB, + "HUB API", + NULL, /* driver_name_list */ + 0, /* nb_driver_names */ + NULL, /* init */ + NULL, /* exit */ + NULL, /* open */ + NULL, /* close */ + NULL, /* configure_endpoints */ + NULL, /* claim_interface */ + NULL, /* set_interface_altsetting */ + NULL, /* release_interface */ + NULL, /* clear_halt */ + NULL, /* reset_device */ + NULL, /* submit_bulk_transfer */ + NULL, /* submit_iso_transfer */ + NULL, /* submit_control_transfer */ + NULL, /* cancel_transfer */ + NULL, /* copy_transfer_data */ + NULL, /* endpoint_supports_raw_io */ + NULL, /* endpoint_set_raw_io */ + NULL, /* get_max_raw_io_transfer_size */ + }, + { + USB_API_COMPOSITE, + "Composite API", + composite_driver_names, + ARRAYSIZE(composite_driver_names), + NULL, /* init */ + NULL, /* exit */ + composite_open, + composite_close, + NULL, /* configure_endpoints */ + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_cancel_transfer, + composite_copy_transfer_data, + composite_endpoint_supports_raw_io, + composite_endpoint_set_raw_io, + composite_get_max_raw_io_transfer_size, + }, + { + USB_API_WINUSBX, + "WinUSB-like APIs", + winusbx_driver_names, + ARRAYSIZE(winusbx_driver_names), + winusbx_init, + winusbx_exit, + winusbx_open, + winusbx_close, + winusbx_configure_endpoints, + winusbx_claim_interface, + winusbx_set_interface_altsetting, + winusbx_release_interface, + winusbx_clear_halt, + winusbx_reset_device, + winusbx_submit_bulk_transfer, + winusbx_submit_iso_transfer, + winusbx_submit_control_transfer, + winusbx_cancel_transfer, + winusbx_copy_transfer_data, + winusbx_endpoint_supports_raw_io, + winusbx_endpoint_set_raw_io, + winusbx_get_max_raw_io_transfer_size, + }, + { + USB_API_HID, + "HID API", + hid_driver_names, + ARRAYSIZE(hid_driver_names), + hid_init, + hid_exit, + hid_open, + hid_close, + NULL, /* configure_endpoints */ + hid_claim_interface, + hid_set_interface_altsetting, + hid_release_interface, + hid_clear_halt, + hid_reset_device, + hid_submit_bulk_transfer, + NULL, /* submit_iso_transfer */ + hid_submit_control_transfer, + NULL, /* cancel_transfer */ + hid_copy_transfer_data, + NULL, /* endpoint_supports_raw_io */ + NULL, /* endpoint_set_raw_io */ + NULL, /* get_max_raw_io_transfer_size */ + }, +}; + + +/* + * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions + */ +#define WinUSB_Set(h, fn, required) \ + do { \ + WinUSBX[SUB_API_WINUSB].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn); \ + if (required && (WinUSBX[SUB_API_WINUSB].fn == NULL)) { \ + usbi_err(ctx, "GetProcAddress() failed for WinUsb_%s", #fn); \ + goto cleanup_winusb; \ + } \ + } while (0) + +#define libusbK_Set(sub_api, fn, required) \ + do { \ + pLibK_GetProcAddress((PVOID *)&WinUSBX[sub_api].fn, sub_api, KUSB_FNID_##fn); \ + if (required && (WinUSBX[sub_api].fn == NULL)) { \ + usbi_err(ctx, "LibK_GetProcAddress() failed for LibK_%s", #fn); \ + goto cleanup_libusbk; \ + } \ + } while (0) + +static bool winusbx_init(struct libusb_context *ctx) +{ + HMODULE hWinUSB, hlibusbK; + + hWinUSB = load_system_library(ctx, "WinUSB"); + if (hWinUSB != NULL) { + WinUSB_Set(hWinUSB, AbortPipe, true); + WinUSB_Set(hWinUSB, ControlTransfer, true); + WinUSB_Set(hWinUSB, FlushPipe, true); + WinUSB_Set(hWinUSB, Free, true); + WinUSB_Set(hWinUSB, GetAssociatedInterface, true); + WinUSB_Set(hWinUSB, Initialize, true); + WinUSB_Set(hWinUSB, ReadPipe, true); + WinUSB_Set(hWinUSB, ResetPipe, true); + WinUSB_Set(hWinUSB, SetCurrentAlternateSetting, true); + WinUSB_Set(hWinUSB, SetPipePolicy, true); + WinUSB_Set(hWinUSB, GetPipePolicy, true); + WinUSB_Set(hWinUSB, WritePipe, true); + + // Check for isochronous transfers support (available starting with Windows 8.1) + WinUSB_Set(hWinUSB, ReadIsochPipeAsap, false); + if (WinUSBX[SUB_API_WINUSB].ReadIsochPipeAsap != NULL) { + WinUSB_Set(hWinUSB, QueryPipeEx, true); + WinUSB_Set(hWinUSB, RegisterIsochBuffer, true); + WinUSB_Set(hWinUSB, UnregisterIsochBuffer, true); + WinUSB_Set(hWinUSB, WriteIsochPipeAsap, true); + } + + WinUSBX[SUB_API_WINUSB].hDll = hWinUSB; + + usbi_info(ctx, "WinUSB DLL available (%s isoch support)", + (WinUSBX[SUB_API_WINUSB].ReadIsochPipeAsap != NULL) ? "with" : "without"); + +cleanup_winusb: + if (WinUSBX[SUB_API_WINUSB].hDll == NULL) { + usbi_err(ctx, "failed to initialize WinUSB"); + memset(&WinUSBX[SUB_API_WINUSB], 0, sizeof(WinUSBX[SUB_API_WINUSB])); + FreeLibrary(hWinUSB); + hWinUSB = NULL; + } + } else { + usbi_info(ctx, "WinUSB DLL is not available"); + } + + hlibusbK = load_system_library(ctx, "libusbK"); + if (hlibusbK != NULL) { + LibK_GetVersion_t pLibK_GetVersion; + LibK_GetProcAddress_t pLibK_GetProcAddress; + int sub_api = 0; + + pLibK_GetVersion = (LibK_GetVersion_t)GetProcAddress(hlibusbK, "LibK_GetVersion"); + if (pLibK_GetVersion != NULL) { + KLIB_VERSION LibK_Version; + + pLibK_GetVersion(&LibK_Version); + 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(ctx, "libusbK DLL found, version unknown"); + } + + pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(hlibusbK, "LibK_GetProcAddress"); + if (pLibK_GetProcAddress == NULL) { + usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL"); + goto cleanup_libusbk; + } + + // NB: The below for loop works because the sub_api value for WinUSB + // is a higher value than that of libusbK and libusb0 + for (; sub_api < SUB_API_WINUSB; sub_api++) { + libusbK_Set(sub_api, AbortPipe, true); + libusbK_Set(sub_api, ControlTransfer, true); + libusbK_Set(sub_api, FlushPipe, true); + libusbK_Set(sub_api, Free, true); + libusbK_Set(sub_api, GetAssociatedInterface, true); + libusbK_Set(sub_api, Initialize, true); + libusbK_Set(sub_api, ReadPipe, true); + libusbK_Set(sub_api, ResetPipe, true); + libusbK_Set(sub_api, SetCurrentAlternateSetting, true); + libusbK_Set(sub_api, SetPipePolicy, true); + libusbK_Set(sub_api, WritePipe, true); + + // Optional isochronous support + libusbK_Set(sub_api, IsoReadPipe, false); + if (WinUSBX[sub_api].IsoReadPipe != NULL) + libusbK_Set(sub_api, IsoWritePipe, true); + + // Optional device reset support + libusbK_Set(sub_api, ResetDevice, false); + + WinUSBX[sub_api].hDll = hlibusbK; + } + +cleanup_libusbk: + if (sub_api < SUB_API_WINUSB) { + usbi_err(ctx, "failed to initialize libusbK"); + while (sub_api >= 0) { + memset(&WinUSBX[sub_api], 0, sizeof(WinUSBX[sub_api])); + sub_api--; + } + FreeLibrary(hlibusbK); + hlibusbK = NULL; + } + } else { + usbi_info(ctx, "libusbK DLL is not available"); + } + + if ((hWinUSB == NULL) && (hlibusbK == NULL)) { + usbi_warn(ctx, "neither WinUSB nor libusbK DLLs were found, " + "you will not be able to access devices outside of enumeration"); + return false; + } + + return true; +} + +static void winusbx_exit(void) +{ + bool loaded = false; + HMODULE hDll; + + hDll = WinUSBX[SUB_API_LIBUSBK].hDll; + if (hDll != NULL) { + FreeLibrary(hDll); + loaded = true; + } + + hDll = WinUSBX[SUB_API_WINUSB].hDll; + if (hDll != NULL) { + FreeLibrary(hDll); + loaded = true; + } + + // Reset the WinUSBX API structures if something was loaded + if (loaded) + memset(&WinUSBX, 0, sizeof(WinUSBX)); +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int 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 = get_winusb_device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // WinUSB requires a separate handle for each interface + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSBX)) { + 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()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + + handle_priv->interface_handle[i].dev_handle = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void winusbx_close(int sub_api, struct libusb_device_handle *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, ai; + + if (sub_api == SUB_API_NOTSET) + sub_api = priv->sub_api; + + if (WinUSBX[sub_api].hDll == NULL) + return; + + if (priv->apib->id == USB_API_COMPOSITE) { + // If this is a composite device, just free and close any WinUSB-like + // interfaces that are not part of an associated group + // (each is independent and not associated with another). + // For associated interface groupings, free interfaces that + // are NOT the first within that group (i.e. not bFirstInterface), + // then free & close bFirstInterface last. + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) { + if (priv->usb_interface[i].num_associated_interfaces == 0) { + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[i].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } else { + if (i==priv->usb_interface[i].first_associated_interface) { + //first free all handles for all *other* associated interfaces + for (ai = 1; ai < priv->usb_interface[i].num_associated_interfaces; ai++) { + handle = handle_priv->interface_handle[i + ai].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + } + + //free & close bFirstInterface + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[i].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } + } + } + } + } else { + // If this is a WinUSB device, free all interfaces above interface 0, + // then free and close interface 0 last + for (i = 1; i < USB_MAXINTERFACES; i++) { + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + } + handle = handle_priv->interface_handle[0].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[0].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(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 = 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; + ULONG timeout = 0; + uint8_t endpoint_address; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // With handle and endpoints set (in parent), we can setup the default pipe properties + // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx + for (i = -1; i < priv->usb_interface[iface].nb_endpoints; i++) { + endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i]; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) + 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(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(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(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(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(HANDLE_CTX(dev_handle), "failed to enable ISO_ALWAYS_START_ASAP for endpoint %02X", endpoint_address); + } + } + + return LIBUSB_SUCCESS; +} + +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 = 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); + bool is_associated_interface = (priv->usb_interface[iface].num_associated_interfaces != 0); + HDEVINFO dev_info; + char *dev_interface_path = NULL; + char *dev_interface_path_guid_start; + char filter_path[] = "\\\\.\\libusb0-0000"; + bool found_filter = false; + HANDLE file_handle, winusb_handle; + DWORD err, _index; + int r; + uint8_t initialized_iface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // If the device is composite, but using the default Windows composite parent driver (usbccgp) + // or if it's the first WinUSB-like interface, we get a handle through Initialize(). + // If it's an associated interface, and is the first one (iface==bFirstInterface), we also + // want to get the handle through Initialize(). If it's an associated interface, and NOT + // the first one, we want to direct control to the 'else' where the handle will be obtained + // via GetAssociatedInterface(). + if (((is_using_usbccgp) || (iface == 0)) && + (!is_associated_interface || (iface==priv->usb_interface[iface].first_associated_interface))) { + // composite device (independent interfaces) or interface 0 + file_handle = handle_priv->interface_handle[iface].dev_handle; + if (!HANDLE_VALID(file_handle)) + return LIBUSB_ERROR_NOT_FOUND; + + if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + err = GetLastError(); + switch (err) { + case ERROR_BAD_COMMAND: + // The device was disconnected + usbi_err(ctx, "could not access interface %u: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + default: + // it may be that we're using the libusb0 filter driver. + // TODO: can we move this whole business into the K/0 DLL? + r = LIBUSB_SUCCESS; + for (_index = 0; ; _index++) { + safe_free(dev_interface_path); + + if (found_filter) + break; + + r = get_interface_details_filter(ctx, &dev_info, _index, filter_path, &dev_interface_path); + if ((r != LIBUSB_SUCCESS) || (dev_interface_path == NULL)) + break; + + // ignore GUID part + dev_interface_path_guid_start = strchr(dev_interface_path, '{'); + if (dev_interface_path_guid_start == NULL) + continue; + *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, 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 + CloseHandle(handle_priv->interface_handle[iface].dev_handle); + handle_priv->interface_handle[iface].dev_handle = file_handle; + found_filter = true; + } else { + usbi_err(ctx, "could not initialize filter driver for %s", filter_path); + CloseHandle(file_handle); + } + } else { + usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0)); + } + } + } + if (r != LIBUSB_SUCCESS) + return r; + if (!found_filter) { + usbi_err(ctx, "could not access interface %u: %s", iface, windows_error_str(err)); + return LIBUSB_ERROR_ACCESS; + } + } + } + handle_priv->interface_handle[iface].api_handle = winusb_handle; + } else { + if (is_associated_interface) { + initialized_iface = priv->usb_interface[iface].first_associated_interface; + if (iface <= initialized_iface) { + usbi_err(ctx, "invalid associated index. iface=%u, initialized iface=%u", iface, initialized_iface); + return LIBUSB_ERROR_NOT_FOUND; + } + } else { + initialized_iface = 0; + } + + // For all other interfaces, use GetAssociatedInterface() + winusb_handle = handle_priv->interface_handle[initialized_iface].api_handle; + // It is a requirement for multiple interface devices on Windows that, to you + // must first claim the first interface before you claim the others + if (!HANDLE_VALID(winusb_handle)) { + file_handle = handle_priv->interface_handle[initialized_iface].dev_handle; + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[initialized_iface].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface %u (required to claim %u with WinUSB)", initialized_iface, iface); + } else { + usbi_warn(ctx, "failed to auto-claim interface %u (required to claim %u with WinUSB): %s", + initialized_iface, iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1 - initialized_iface), + &handle_priv->interface_handle[iface].api_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + switch (GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %u: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[initialized_iface].dev_handle; + } + usbi_dbg(ctx, "claimed interface %u", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface) +{ + 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; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if (!HANDLE_VALID(winusb_handle)) + return LIBUSB_ERROR_NOT_FOUND; + + WinUSBX[sub_api].Free(winusb_handle); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +/* + * Return the first valid interface (of the same API type), for control transfers + */ +static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id) +{ + 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(HANDLE_CTX(dev_handle), "unsupported API ID"); + return -1; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (HANDLE_VALID(handle_priv->interface_handle[i].dev_handle) + && HANDLE_VALID(handle_priv->interface_handle[i].api_handle) + && (priv->usb_interface[i].apib->id == api_id)) + return i; + } + + return -1; +} + +/* +* Check a specific interface is valid (of the same API type), for control transfers +*/ +static int check_valid_interface(struct libusb_device_handle *dev_handle, unsigned short interface, int api_id) +{ + 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(HANDLE_CTX(dev_handle), "unsupported API ID"); + return -1; + } + + // try the requested interface + if (HANDLE_VALID(handle_priv->interface_handle[interface].dev_handle) + && HANDLE_VALID(handle_priv->interface_handle[interface].api_handle) + && (priv->usb_interface[interface].apib->id == api_id)) + return interface; + + return -1; +} + +/* + * Lookup interface by endpoint address. -1 if not found + */ +static int interface_by_endpoint(struct winusb_device_priv *priv, + struct winusb_device_handle_priv *handle_priv, uint8_t endpoint_address) +{ + int i, j; + int result = -1; + + // priv->usb_interface[].endpoint and .nb_endpoints are written + // under interface_lock by claim/altsetting and the enumeration + // setup paths. Take the lock so we don't read a freed endpoint + // pointer or an inconsistent (nb_endpoints, endpoint[]) pair. + usbi_mutex_lock(&priv->interface_lock); + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (!HANDLE_VALID(handle_priv->interface_handle[i].api_handle)) + continue; + if (priv->usb_interface[i].endpoint == NULL) + continue; + for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) { + if (priv->usb_interface[i].endpoint[j] == endpoint_address) { + result = i; + goto out; + } + } + } + +out: + usbi_mutex_unlock(&priv->interface_lock); + return result; +} + +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + 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 = get_winusb_device_handle_priv(transfer->dev_handle); + PWINUSB_SETUP_PACKET setup = (PWINUSB_SETUP_PACKET)transfer->buffer; + ULONG size, transferred; + HANDLE winusb_handle; + OVERLAPPED *overlapped; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + // Windows places upper limits on the control transfer size + // 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; + + if ((setup->RequestType & 0x1F) == LIBUSB_RECIPIENT_INTERFACE) + current_interface = check_valid_interface(transfer->dev_handle, setup->Index & 0xff, USB_API_WINUSBX); + else + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS) + return LIBUSB_ERROR_NOT_FOUND; + } + + 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); + + // Sending of set configuration control requests from WinUSB creates issues, except when using libusb0.sys + if (sub_api != SUB_API_LIBUSB0 + && (LIBUSB_REQ_TYPE(setup->RequestType) == LIBUSB_REQUEST_TYPE_STANDARD) + && (setup->Request == LIBUSB_REQUEST_SET_CONFIGURATION)) { + if (setup->Value != priv->active_config) { + usbi_warn(TRANSFER_CTX(transfer), "cannot set configuration other than the default one"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + windows_force_sync_completion(itransfer, 0); + } else { + 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); + } + } + + 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 = get_winusb_device_handle_priv(dev_handle); + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if (!HANDLE_VALID(winusb_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "interface must be claimed first"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, altsetting)) { + usbi_err(HANDLE_CTX(dev_handle), "SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + + +static void WINAPI winusbx_native_iso_transfer_continue_stream_callback(struct libusb_transfer *transfer) +{ + // If this callback is invoked, this means that we attempted to set ContinueStream + // to TRUE when calling Read/WriteIsochPipeAsap in winusbx_submit_iso_transfer(). + // The role of this callback is to fallback to ContinueStream = FALSE if the transfer + // did not succeed. + + struct winusb_transfer_priv *transfer_priv = + get_winusb_transfer_priv(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)); + bool fallback = (transfer->status != LIBUSB_TRANSFER_COMPLETED); + int idx; + + // Restore the user callback + transfer->callback = transfer_priv->iso_user_callback; + + for (idx = 0; idx < transfer->num_iso_packets && !fallback; idx++) { + if (transfer->iso_packet_desc[idx].status != LIBUSB_TRANSFER_COMPLETED) + fallback = true; + } + + if (!fallback) { + // If the transfer was successful, we restore the user callback and call it. + if (transfer->callback) + transfer->callback(transfer); + } else { + // If the transfer wasn't successful we reschedule the transfer while forcing it + // not to continue the stream. This might results in a 5-ms delay. + transfer_priv->iso_break_stream = TRUE; + libusb_submit_transfer(transfer); + } +} +static int winusbx_submit_iso_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 = 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; + BOOL ret; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(TRANSFER_CTX(transfer), "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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 ((sub_api == SUB_API_LIBUSBK) || (sub_api == SUB_API_LIBUSB0)) { + int i; + UINT offset; + size_t iso_ctx_size; + PKISO_CONTEXT iso_context; + + if (WinUSBX[sub_api].IsoReadPipe == NULL) { + usbi_warn(TRANSFER_CTX(transfer), "libusbK DLL does not support isoch transfers"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + iso_ctx_size = sizeof(KISO_CONTEXT) + (transfer->num_iso_packets * sizeof(KISO_PACKET)); + transfer_priv->iso_context = iso_context = calloc(1, iso_ctx_size); + if (transfer_priv->iso_context == NULL) + return LIBUSB_ERROR_NO_MEM; + + // start ASAP + iso_context->StartFrame = 0; + iso_context->NumberOfPackets = (SHORT)transfer->num_iso_packets; + + // convert the transfer packet lengths to iso_packet offsets + offset = 0; + for (i = 0; i < transfer->num_iso_packets; i++) { + iso_context->IsoPackets[i].offset = offset; + offset += transfer->iso_packet_desc[i].length; + } + + if (IS_XFERIN(transfer)) { + 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(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); + } + + if (!ret && GetLastError() != ERROR_IO_PENDING) { + usbi_err(TRANSFER_CTX(transfer), "IsoReadPipe/IsoWritePipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; + } else if (sub_api == SUB_API_WINUSB) { + WINUSB_PIPE_INFORMATION_EX pipe_info_ex = { 0 }; + WINUSB_ISOCH_BUFFER_HANDLE buffer_handle; + ULONG iso_transfer_size_multiple; + int out_transfer_length = 0; + int idx; + + // Depending on the version of Microsoft WinUSB, isochronous transfers may not be supported. + if (WinUSBX[sub_api].ReadIsochPipeAsap == NULL) { + usbi_warn(TRANSFER_CTX(transfer), "WinUSB DLL does not support isoch transfers"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + if (sizeof(struct libusb_iso_packet_descriptor) != sizeof(USBD_ISO_PACKET_DESCRIPTOR)) { + usbi_err(TRANSFER_CTX(transfer), "size of WinUsb and libusb isoch packet descriptors don't match"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + // Query the pipe extended information to find the pipe index corresponding to the endpoint. + // nb_endpoints and current_altsetting are written under interface_lock by claim/altsetting + // and enumeration setup; take the lock so the loop bound and the QueryPipeEx altsetting + // argument come from a consistent snapshot. + { + int nb_endpoints; + uint8_t altsetting; + + usbi_mutex_lock(&priv->interface_lock); + nb_endpoints = priv->usb_interface[current_interface].nb_endpoints; + altsetting = (uint8_t)priv->usb_interface[current_interface].current_altsetting; + usbi_mutex_unlock(&priv->interface_lock); + + for (idx = 0; idx < nb_endpoints; ++idx) { + ret = WinUSBX[sub_api].QueryPipeEx(winusb_handle, altsetting, (UCHAR)idx, &pipe_info_ex); + if (!ret) { + usbi_err(TRANSFER_CTX(transfer), "couldn't query interface settings for USB pipe with index %d. Error: %s", idx, windows_error_str(0)); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (pipe_info_ex.PipeId == transfer->endpoint && pipe_info_ex.PipeType == UsbdPipeTypeIsochronous) + break; + } + + // Make sure we found the index. + if (idx == nb_endpoints) { + usbi_err(TRANSFER_CTX(transfer), "couldn't find isoch endpoint 0x%02x", transfer->endpoint); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + if (IS_XFERIN(transfer)) { + int interval = pipe_info_ex.Interval; + + // For high-speed and SuperSpeed device, the interval is 2**(bInterval-1). + if (transfer->dev_handle->dev->speed >= LIBUSB_SPEED_HIGH) + interval = (1 << (pipe_info_ex.Interval - 1)); + + // 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... + 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; + } + } else { + // If this is an OUT transfer, we make sure the isoch packets are contiguous as this isn't supported otherwise. + bool size_should_be_zero = false; + + for (idx = 0; idx < transfer->num_iso_packets; ++idx) { + if ((size_should_be_zero && transfer->iso_packet_desc[idx].length != 0) || + (transfer->iso_packet_desc[idx].length != pipe_info_ex.MaximumBytesPerInterval && idx + 1 < transfer->num_iso_packets && transfer->iso_packet_desc[idx + 1].length > 0)) { + usbi_err(TRANSFER_CTX(transfer), "isoch packets for OUT transfer with WinUSB must be contiguous in memory"); + return LIBUSB_ERROR_INVALID_PARAM; + } + + size_should_be_zero = (transfer->iso_packet_desc[idx].length == 0); + out_transfer_length += transfer->iso_packet_desc[idx].length; + } + } + + if (transfer_priv->isoch_buffer_handle != NULL) { + if (WinUSBX[sub_api].UnregisterIsochBuffer(transfer_priv->isoch_buffer_handle)) { + transfer_priv->isoch_buffer_handle = NULL; + } else { + usbi_err(TRANSFER_CTX(transfer), "failed to unregister WinUSB isoch buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } + + // Register the isoch buffer to the operating system. + ret = WinUSBX[sub_api].RegisterIsochBuffer(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, &buffer_handle); + if (!ret) { + usbi_err(TRANSFER_CTX(transfer), "failed to register WinUSB isoch buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_MEM; + } + + // Important note: the WinUSB_Read/WriteIsochPipeAsap API requires a ContinueStream parameter that tells whether the isochronous + // stream must be continued or if the WinUSB driver can schedule the transfer at its convenience. Profiling subsequent transfers + // with ContinueStream = FALSE showed that 5 frames, i.e. about 5 milliseconds, were left empty between each transfer. This + // is critical as this greatly diminish the achievable isochronous bandwidth. We solved the problem using the following strategy: + // - Transfers are first scheduled with ContinueStream = TRUE and with winusbx_iso_transfer_continue_stream_callback as user callback. + // - If the transfer succeeds, winusbx_iso_transfer_continue_stream_callback restore the user callback and calls its. + // - If the transfer fails, winusbx_iso_transfer_continue_stream_callback reschedule the transfer and force ContinueStream = FALSE. + if (!transfer_priv->iso_break_stream) { + transfer_priv->iso_user_callback = transfer->callback; + transfer->callback = winusbx_native_iso_transfer_continue_stream_callback; + } + + // Initiate the transfers. + if (IS_XFERIN(transfer)) + ret = WinUSBX[sub_api].ReadIsochPipeAsap(buffer_handle, 0, transfer->length, !transfer_priv->iso_break_stream, transfer->num_iso_packets, (PUSBD_ISO_PACKET_DESCRIPTOR)transfer->iso_packet_desc, overlapped); + else + ret = WinUSBX[sub_api].WriteIsochPipeAsap(buffer_handle, 0, out_transfer_length, !transfer_priv->iso_break_stream, overlapped); + + if (!ret && GetLastError() != ERROR_IO_PENDING) { + usbi_err(TRANSFER_CTX(transfer), "ReadIsochPipeAsap/WriteIsochPipeAsap failed: %s", windows_error_str(0)); + if (!WinUSBX[sub_api].UnregisterIsochBuffer(buffer_handle)) + usbi_warn(TRANSFER_CTX(transfer), "failed to unregister WinUSB isoch buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + // Restore the ContinueStream parameter to TRUE. + transfer_priv->iso_break_stream = FALSE; + + transfer_priv->isoch_buffer_handle = buffer_handle; + + return LIBUSB_SUCCESS; + } else { + PRINT_UNSUPPORTED_API(winusbx_submit_iso_transfer); + return LIBUSB_ERROR_NOT_SUPPORTED; + } +} + +static int winusbx_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 = 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; + BOOL ret; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(TRANSFER_CTX(transfer), "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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)) { + // Note: We don't need to handle transfers to pipes with RAW_IO enabled differently, + // as ReadPipe() already fails if the length argument doesn't satisfy the RAW_IO requirements. + 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 { + // 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); + } + + if (!ret && GetLastError() != ERROR_IO_PENDING) { + usbi_err(TRANSFER_CTX(transfer), "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + 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 = 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; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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)) { + usbi_err(HANDLE_CTX(dev_handle), "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +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 = 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; + HANDLE handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + 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)) { + usbi_err(TRANSFER_CTX(transfer), "AbortPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper + * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx): + * "WinUSB does not support host-initiated reset port and cycle port operations" and + * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the + * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is + * cycle the pipes (and even then, the control pipe can not be reset using WinUSB) + */ +// TODO: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?) +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *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; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // Reset any available pipe (except control). + // priv->usb_interface[].endpoint and .nb_endpoints are written under + // interface_lock by claim/altsetting and the enumeration setup paths; + // take the lock so we don't read a freed endpoint pointer or an + // inconsistent (nb_endpoints, endpoint[]) pair. + usbi_mutex_lock(&priv->interface_lock); + for (i = 0; i < USB_MAXINTERFACES; i++) { + 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(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)); + + // FlushPipe seems to fail on OUT pipes + if (IS_EPIN(priv->usb_interface[i].endpoint[j]) + && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))) + usbi_err(HANDLE_CTX(dev_handle), "FlushPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) + usbi_err(HANDLE_CTX(dev_handle), "ResetPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + } + } + usbi_mutex_unlock(&priv->interface_lock); + + // libusbK & libusb0 have the ability to issue an actual device reset + if ((sub_api != SUB_API_WINUSB) && (WinUSBX[sub_api].ResetDevice != NULL)) { + winusb_handle = handle_priv->interface_handle[0].api_handle; + if (HANDLE_VALID(winusb_handle)) + WinUSBX[sub_api].ResetDevice(winusb_handle); + } + + return LIBUSB_SUCCESS; +} + +static enum libusb_transfer_status winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer); + 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++) { + 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; + } + transfer->iso_packet_desc[i].status = usbd_status_to_libusb_transfer_status(iso_context->IsoPackets[i].status); + } + } else if (sub_api == SUB_API_WINUSB) { + if (IS_XFERIN(transfer)) { + /* Convert isochronous packet descriptor between Windows and libusb representation. + * Both representation are guaranteed to have the same length in bytes.*/ + PUSBD_ISO_PACKET_DESCRIPTOR usbd_iso_packet_desc = (PUSBD_ISO_PACKET_DESCRIPTOR)transfer->iso_packet_desc; + for (i = 0; i < transfer->num_iso_packets; i++) { + unsigned int packet_length = (i < transfer->num_iso_packets - 1) ? (usbd_iso_packet_desc[i + 1].Offset - usbd_iso_packet_desc[i].Offset) : usbd_iso_packet_desc[i].Length; + unsigned int actual_length = usbd_iso_packet_desc[i].Length; + USBD_STATUS status = usbd_iso_packet_desc[i].Status; + + transfer->iso_packet_desc[i].length = packet_length; + transfer->iso_packet_desc[i].actual_length = actual_length; + transfer->iso_packet_desc[i].status = usbd_status_to_libusb_transfer_status(status); + } + } 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 { + // This should only occur if backend is not set correctly or other backend isoc is partially implemented + PRINT_UNSUPPORTED_API(copy_transfer_data); + return LIBUSB_TRANSFER_ERROR; + } + } + + itransfer->transferred += (int)length; + return LIBUSB_TRANSFER_COMPLETED; +} + +static int winusbx_endpoint_supports_raw_io(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint) +{ + struct libusb_context *ctx; + struct winusb_device_handle_priv *handle_priv; + struct winusb_device_priv *priv; + int interface; + HANDLE winusb_handle; + + ctx = HANDLE_CTX(dev_handle); + + if (!ctx) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (endpoint & ~(LIBUSB_ENDPOINT_DIR_MASK | LIBUSB_ENDPOINT_ADDRESS_MASK)) { + usbi_err(ctx, "invalid endpoint 0x%X passed for RAW_IO support query", endpoint); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (!(endpoint & LIBUSB_ENDPOINT_DIR_MASK)) { + usbi_err(ctx, "endpoint 0x%02X is OUT not IN for RAW_IO support query", endpoint); + return LIBUSB_ERROR_INVALID_PARAM; + } + + handle_priv = get_winusb_device_handle_priv(dev_handle); + priv = usbi_get_device_priv(dev_handle->dev); + interface = interface_by_endpoint(priv, handle_priv, (uint8_t) endpoint); + + if (interface < 0) { + usbi_err(ctx, "unable to match endpoint 0x%02X to an open interface for RAW_IO support query", endpoint); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg(ctx, "matched endpoint 0x%02X to interface %d for RAW_IO support query", endpoint, interface); + + if (priv->usb_interface[interface].apib->id != USB_API_WINUSBX) { + usbi_err(ctx, "interface %d is not managed by WinUSB, cannot query RAW_IO support", interface); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + winusb_handle = handle_priv->interface_handle[interface].api_handle; + + if (!HANDLE_VALID(winusb_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "WinUSB handle not valid for interface %d, cannot query RAW_IO support", interface); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // If we made it this far RAW_IO is supported. + return 1; +} + +int winusbx_endpoint_set_raw_io(int sub_api, libusb_device_handle* dev_handle, uint8_t endpoint, int enable) +{ + struct libusb_context *ctx; + struct winusb_device_handle_priv *handle_priv; + struct winusb_device_priv *priv; + UCHAR policy; + int interface; + HANDLE winusb_handle; + + ctx = HANDLE_CTX(dev_handle); + + if (!ctx) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (endpoint & ~(LIBUSB_ENDPOINT_DIR_MASK | LIBUSB_ENDPOINT_ADDRESS_MASK)) { + usbi_err(ctx, "invalid endpoint 0x%X passed, cannot set RAW_IO", endpoint); + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (!(endpoint & LIBUSB_ENDPOINT_DIR_MASK)) { + usbi_err(ctx, "endpoint 0x%02X is OUT not IN, cannot set RAW_IO", endpoint); + return LIBUSB_ERROR_INVALID_PARAM; + } + + handle_priv = get_winusb_device_handle_priv(dev_handle); + priv = usbi_get_device_priv(dev_handle->dev); + interface = interface_by_endpoint(priv, handle_priv, (uint8_t) endpoint); + + if (interface < 0) { + usbi_err(ctx, "unable to match endpoint 0x%02X to an open interface for RAW_IO", endpoint); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg(ctx, "matched endpoint 0x%02X to interface %d", endpoint, interface); + + if (priv->usb_interface[interface].apib->id != USB_API_WINUSBX) { + usbi_err(ctx, "interface %d is not managed by WinUSB, cannot set RAW_IO", interface); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + winusb_handle = handle_priv->interface_handle[interface].api_handle; + + if (!HANDLE_VALID(winusb_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "WinUSB handle not valid for interface %d, cannot set RAW_IO", interface); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_WINUSBX_AVAILABLE(sub_api); + + policy = enable != 0; + + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, (UCHAR) endpoint, + RAW_IO, sizeof(UCHAR), &policy)) { + DWORD error = GetLastError(); + usbi_err(ctx, "failed to change RAW_IO for endpoint %02X: %s", endpoint, windows_error_str(error)); + switch (error) { + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_PARAMETER: + return LIBUSB_ERROR_INVALID_PARAM; + case ERROR_NOT_ENOUGH_MEMORY: + return LIBUSB_ERROR_NO_MEM; + default: + return LIBUSB_ERROR_OTHER; + } + } + + usbi_dbg(ctx, "%s RAW_IO for endpoint %02X", enable ? "enabled" : "disabled", endpoint); + + return LIBUSB_SUCCESS; +} + +static int winusbx_get_max_raw_io_transfer_size(int sub_api, struct libusb_device_handle* dev_handle, uint8_t endpoint) +{ + struct libusb_context *ctx; + struct winusb_device_handle_priv *handle_priv; + struct winusb_device_priv *priv; + int interface; + HANDLE winusb_handle; + ULONG max_transfer_size = 0; + + ctx = HANDLE_CTX(dev_handle); + + if (!ctx) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (endpoint & ~(LIBUSB_ENDPOINT_DIR_MASK | LIBUSB_ENDPOINT_ADDRESS_MASK)) { + usbi_err(ctx, "invalid endpoint 0x%X passed, cannot get maximum transfer size for RAW_IO", endpoint); + return LIBUSB_ERROR_INVALID_PARAM; + } + + handle_priv = get_winusb_device_handle_priv(dev_handle); + priv = usbi_get_device_priv(dev_handle->dev); + interface = interface_by_endpoint(priv, handle_priv, (uint8_t) endpoint); + + if (interface < 0) { + usbi_err(ctx, "unable to match endpoint 0x%02X to an open interface, cannot get maximum transfer size for RAW_IO", endpoint); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg(ctx, "matched endpoint 0x%02X to interface %d", endpoint, interface); + + if (priv->usb_interface[interface].apib->id != USB_API_WINUSBX) { + usbi_err(ctx, "interface %d is not managed by WinUSB, cannot get maximum transfer size for RAW_IO", interface); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + winusb_handle = handle_priv->interface_handle[interface].api_handle; + + if (!HANDLE_VALID(winusb_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "WinUSB handle not valid for interface %d, cannot get maximum transfer size for RAW_IO", interface); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_WINUSBX_AVAILABLE(sub_api); + + ULONG size = sizeof(ULONG); + if (!WinUSBX[sub_api].GetPipePolicy(winusb_handle, (UCHAR) endpoint, + MAXIMUM_TRANSFER_SIZE, &size, &max_transfer_size)) { + DWORD error = GetLastError(); + usbi_err(ctx, "failed to get RAW_IO maximum transfer size for endpoint 0x%02X: %s", endpoint, windows_error_str(error)); + switch (error) { + case ERROR_INVALID_HANDLE: + return LIBUSB_ERROR_INVALID_PARAM; + default: + return LIBUSB_ERROR_OTHER; + } + } + + usbi_dbg(ctx, "maximum transfer size for endpoint 0x%02X is %lu", endpoint, max_transfer_size); + + return (int)max_transfer_size; +} + +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv *dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv *dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + + while (str[i] && (str[i] != 0x409)) + i++; + + return i; +} + +static int _hid_get_device_descriptor(struct libusb_device *dev, struct hid_device_priv *hid_priv, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + /* Copy some values from the cached device descriptor + * because we cannot get them through HID */ + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = dev->device_descriptor.bcdUSB; + d.bDeviceClass = dev->device_descriptor.bDeviceClass; + d.bDeviceSubClass = dev->device_descriptor.bDeviceSubClass; + d.bDeviceProtocol = dev->device_descriptor.bDeviceProtocol; + d.bMaxPacketSize0 = dev->device_descriptor.bMaxPacketSize0; + d.idVendor = (uint16_t)hid_priv->vid; + d.idProduct = (uint16_t)hid_priv->pid; + d.bcdDevice = dev->device_descriptor.bcdDevice; + d.iManufacturer = hid_priv->string_index[0]; + d.iProduct = hid_priv->string_index[1]; + d.iSerialNumber = hid_priv->string_index[2]; + d.bNumConfigurations = dev->device_descriptor.bNumConfigurations; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv *hid_priv, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (hid_priv->input_report_size) + num_endpoints++; + if (hid_priv->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t)config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(hid_priv, hd, &tmp_size); + + if (hid_priv->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = hid_priv->input_report_size - 1; + ed->bInterval = 10; + ed = (struct libusb_endpoint_descriptor *)((char *)ed + LIBUSB_DT_ENDPOINT_SIZE); + } + + if (hid_priv->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = hid_priv->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv *hid_priv, int _index, + void *data, size_t *size, HANDLE hid_handle) +{ + void *tmp = NULL; + WCHAR string[MAX_USB_STRING_LENGTH]; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = {0x09, 0x04}; + + if (_index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid) + 2; + } else { + for (i = 0; i < 3; i++) { + if (_index == (hid_priv->string_index[i])) { + tmp = hid_priv->string[i]; + tmp_size = (_hid_wcslen(hid_priv->string[i]) + 1) * sizeof(WCHAR); + break; + } + } + + if (i == 3) { + if (!HidD_GetIndexedString(hid_handle, _index, string, sizeof(string))) + return LIBUSB_ERROR_INVALID_PARAM; + tmp = string; + tmp_size = (_hid_wcslen(string) + 1) * sizeof(WCHAR); + } + } + + if (!tmp_size) + return LIBUSB_ERROR_INVALID_PARAM; + + if (tmp_size < *size) + *size = tmp_size; + + // 2 byte header + ((uint8_t *)data)[0] = (uint8_t)*size; + ((uint8_t *)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t *)data + 2, tmp, *size - 2); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv *hid_priv, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(hid_priv, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv *hid_priv, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page */ + d[i++] = 0x06; d[i++] = hid_priv->usagePage & 0xFF; d[i++] = hid_priv->usagePage >> 8; + /* usage */ + d[i++] = 0x09; d[i++] = (uint8_t)hid_priv->usage; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (hid_priv->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)hid_priv->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (hid_priv->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)hid_priv->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (hid_priv->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)hid_priv->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct libusb_device *dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + UNUSED(recipient); + + switch (type) { + case LIBUSB_DT_DEVICE: + usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, priv->hid, data, size); + case 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(DEVICE_CTX(dev), "LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(priv->hid, _index, data, size, hid_handle); + case 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(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(DEVICE_CTX(dev), "LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + + usbi_warn(DEVICE_CTX(dev), "unsupported"); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int _hid_get_report(struct libusb_device *dev, HANDLE hid_handle, int id, void *data, + struct winusb_transfer_priv *tp, size_t size, OVERLAPPED *overlapped, int report_type) +{ + DWORD ioctl_code, expected_size = (DWORD)size; + uint8_t *buf; + + if (tp->hid_buffer != NULL) + usbi_err(DEVICE_CTX(dev), "program assertion failed - hid_buffer is not NULL"); + + if ((size == 0) || (size > MAX_HID_REPORT_SIZE)) { + usbi_warn(DEVICE_CTX(dev), "invalid size (%"PRIuPTR")", (uintptr_t)size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_warn(DEVICE_CTX(dev), "unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // Add a trailing byte to detect overflows + buf = calloc(1, expected_size + 1); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + buf[0] = (uint8_t)id; // Must be set always + 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, + buf, expected_size + 1, NULL, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(DEVICE_CTX(dev), "failed to read HID Report: %s", windows_error_str(0)); + free(buf); + return LIBUSB_ERROR_IO; + } + } + + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer + tp->hid_expected_size = expected_size; + + return LIBUSB_SUCCESS; +} + +static int _hid_set_report(struct libusb_device *dev, HANDLE hid_handle, int id, void *data, + struct winusb_transfer_priv *tp, size_t size, OVERLAPPED *overlapped, int report_type) +{ + DWORD ioctl_code, write_size = (DWORD)size; + // If an id is reported, we must allow MAX_HID_REPORT_SIZE + 1 + size_t max_report_size = MAX_HID_REPORT_SIZE + (id ? 1 : 0); + uint8_t *buf; + + if (tp->hid_buffer != NULL) + usbi_err(DEVICE_CTX(dev), "program assertion failed - hid_buffer is not NULL"); + + if ((size == 0) || (size > max_report_size)) { + usbi_warn(DEVICE_CTX(dev), "invalid size (%"PRIuPTR")", (uintptr_t)size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_warn(DEVICE_CTX(dev), "unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + 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) + write_size++; + + buf = malloc(write_size); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, size); + if (buf[0] != id) + usbi_warn(DEVICE_CTX(dev), "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, NULL, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(DEVICE_CTX(dev), "failed to write HID Output Report: %s", windows_error_str(0)); + free(buf); + return LIBUSB_ERROR_IO; + } + } + + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; +} + +static int _hid_class_request(struct libusb_device *dev, HANDLE hid_handle, int request_type, + int request, int value, int _index, void *data, struct winusb_transfer_priv *tp, + size_t size, OVERLAPPED *overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + UNUSED(_index); + + if ((LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE)) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + +/* + * HID API functions + */ +static bool hid_init(struct libusb_context *ctx) +{ + DLL_GET_HANDLE(ctx, hid); + + DLL_LOAD_FUNC(hid, HidD_GetAttributes, true); + DLL_LOAD_FUNC(hid, HidD_GetHidGuid, true); + DLL_LOAD_FUNC(hid, HidD_GetPreparsedData, true); + DLL_LOAD_FUNC(hid, HidD_FreePreparsedData, true); + DLL_LOAD_FUNC(hid, HidD_GetManufacturerString, true); + DLL_LOAD_FUNC(hid, HidD_GetProductString, true); + DLL_LOAD_FUNC(hid, HidD_GetSerialNumberString, true); + DLL_LOAD_FUNC(hid, HidD_GetIndexedString, true); + DLL_LOAD_FUNC(hid, HidP_GetCaps, true); + DLL_LOAD_FUNC(hid, HidD_SetNumInputBuffers, true); + DLL_LOAD_FUNC(hid, HidD_GetPhysicalDescriptor, true); + DLL_LOAD_FUNC(hid, HidD_FlushQueue, true); + DLL_LOAD_FUNC(hid, HidP_GetValueCaps, true); + + return true; +} + +static void hid_exit(void) +{ + DLL_FREE_HANDLE(hid); +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_device *dev = dev_handle->dev; + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + 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; + HIDP_VALUE_CAPS *value_caps; + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + int nb_ids[2]; // zero and nonzero report IDs +#if defined(ENABLE_LOGGING) + const char * const type[3] = {"input", "output", "feature"}; +#endif + + UNUSED(sub_api); + CHECK_HID_AVAILABLE; + + if (priv->hid == NULL) { + usbi_err(HANDLE_CTX(dev_handle), "program assertion failed - private HID structure is uninitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID)) { + hid_handle = 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 + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(HANDLE_CTX(dev_handle), "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + 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()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(HANDLE_CTX(dev_handle), "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i = 32; HidD_SetNumInputBuffers(hid_handle, i); i *= 2); + usbi_dbg(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) { + usbi_err(HANDLE_CTX(dev_handle), "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(HANDLE_CTX(dev_handle), "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j = HidP_Input; j <= HidP_Feature; j++) { + usbi_dbg(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)); + if ((value_caps != NULL) + && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1)) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i = 0; i < (int)size[j]; i++) { + usbi_dbg(HANDLE_CTX(dev_handle), " Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) + nb_ids[1]++; + else + nb_ids[0]++; + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) + usbi_warn(HANDLE_CTX(dev_handle), "program assertion failed - zero and nonzero report IDs used for %s", + type[j]); + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(HANDLE_CTX(dev_handle), " could not process %s report IDs", type[j]); + } + free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Store usage and usagePage values + priv->hid->usage = capabilities.Usage; + priv->hid->usagePage = capabilities.UsagePage; + + // Fetch string descriptors + priv->hid->string_index[0] = dev->device_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], sizeof(priv->hid->string[0])); + else + priv->hid->string[0][0] = 0; + + priv->hid->string_index[1] = dev->device_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) + // 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; + + priv->hid->string_index[2] = dev->device_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], sizeof(priv->hid->string[2])); + else + priv->hid->string[2][0] = 0; + } while (0); + + if (preparsed_data) + HidD_FreePreparsedData(preparsed_data); + + return LIBUSB_SUCCESS; +} + +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + UNUSED(sub_api); + + if (DLL_HANDLE_NAME(hid) == NULL) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(file_handle)) + CloseHandle(file_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 = get_winusb_device_handle_priv(dev_handle); + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + UNUSED(sub_api); + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) + return LIBUSB_ERROR_BUSY; // already claimed + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg(HANDLE_CTX(dev_handle), "claimed interface %u", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface) +{ + 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); + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) +{ + UNUSED(sub_api); + UNUSED(iface); + + CHECK_HID_AVAILABLE; + + if (altsetting != 0) { + usbi_err(HANDLE_CTX(dev_handle), "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct 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 = 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; + OVERLAPPED *overlapped; + int current_interface; + uint8_t config; + size_t size; + int r; + + UNUSED(sub_api); + CHECK_HID_AVAILABLE; + + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(dev_handle, USB_API_HID); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg(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); + + switch (LIBUSB_REQ_TYPE(setup->RequestType)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch (setup->Request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(dev_handle->dev, hid_handle, LIBUSB_REQ_RECIPIENT(setup->RequestType), + (setup->Value >> 8) & 0xFF, setup->Value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = winusb_get_configuration(dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->Value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(TRANSFER_CTX(transfer), "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(0, dev_handle, (uint8_t)setup->Index, (uint8_t)setup->Value); + if (r == LIBUSB_SUCCESS) + r = LIBUSB_COMPLETED; + break; + default: + usbi_warn(TRANSFER_CTX(transfer), "unsupported HID control request"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r = _hid_class_request(dev_handle->dev, hid_handle, setup->RequestType, setup->Request, setup->Value, + setup->Index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + size, overlapped); + break; + default: + usbi_warn(TRANSFER_CTX(transfer), "unsupported HID control request"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + if (r < 0) + return r; + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + windows_force_sync_completion(itransfer, (ULONG)size); + r = LIBUSB_SUCCESS; + } + + return LIBUSB_SUCCESS; +} + +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 = 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; + bool direction_in; + BOOL ret; + int current_interface, length; + + 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); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(TRANSFER_CTX(transfer), "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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); + direction_in = IS_XFERIN(transfer); + + // If report IDs are not in use, an extra prefix byte must be added + if (((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1]))) + length = transfer->length + 1; + else + length = transfer->length; + + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = calloc(1, length + 1); + if (transfer_priv->hid_buffer == NULL) + return LIBUSB_ERROR_NO_MEM; + + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg(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]) + memcpy(transfer_priv->hid_buffer + 1, transfer->buffer, transfer->length); + else + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + + usbi_dbg(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); + } + + if (!ret && GetLastError() != ERROR_IO_PENDING) { + usbi_err(TRANSFER_CTX(transfer), "HID transfer failed: %s", windows_error_str(0)); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + UNUSED(sub_api); + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if (HANDLE_VALID(hid_handle)) + HidD_FlushQueue(hid_handle); + } + + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct 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; + + UNUSED(sub_api); + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + 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 + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(HANDLE_CTX(dev_handle), "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static enum libusb_transfer_status hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer); + enum libusb_transfer_status r = LIBUSB_TRANSFER_COMPLETED; + + UNUSED(sub_api); + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + if (length > 0) { + // First, check for overflow + if ((size_t)length > transfer_priv->hid_expected_size) { + usbi_err(TRANSFER_CTX(transfer), "OVERFLOW!"); + length = (DWORD)transfer_priv->hid_expected_size; + r = LIBUSB_TRANSFER_OVERFLOW; + } + + if (transfer_priv->hid_buffer[0] == 0) { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer + 1, length); + } else { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, length); + } + } + transfer_priv->hid_dest = NULL; + } + // For write, we just need to free the hid buffer + safe_free(transfer_priv->hid_buffer); + } + + itransfer->transferred += (int)length; + return r; +} + + +/* + * Composite API functions + */ +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int i, r = LIBUSB_ERROR_NOT_FOUND; + // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX + 1]; + + UNUSED(sub_api); + + for (i = 0; i < SUB_API_MAX + 1; i++) + available[i] = false; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + switch (priv->usb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle); + if (r != LIBUSB_SUCCESS) + return r; + } + } + + if (available[SUB_API_MAX]) { // HID driver + r = hid_open(SUB_API_NOTSET, dev_handle); + + // On Windows 10 version 1903 (OS Build 18362) and later Windows blocks attempts to + // 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(HANDLE_CTX(dev_handle), "ignoring access denied error while opening HID interface of composite device"); + r = LIBUSB_SUCCESS; + } + } + + return r; +} + +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int i; + // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX + 1]; + + UNUSED(sub_api); + + for (i = 0; i < SUB_API_MAX + 1; i++) + available[i] = false; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + switch (priv->usb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers + if (available[i]) + usb_api_backend[USB_API_WINUSBX].close(i, dev_handle); + } + + if (available[SUB_API_MAX]) // HID driver + hid_close(SUB_API_NOTSET, dev_handle); +} + +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + UNUSED(sub_api); + CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, claim_interface); + + return priv->usb_interface[iface].apib-> + claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + UNUSED(sub_api); + CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, set_interface_altsetting); + + return priv->usb_interface[iface].apib-> + set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting); +} + +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + + UNUSED(sub_api); + CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, release_interface); + + return priv->usb_interface[iface].apib-> + release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + struct libusb_config_descriptor *conf_desc; + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer; + int iface, pass, r; + + UNUSED(sub_api); + + // Interface shouldn't matter for control, but it does in practice, with Windows' + // restrictions with regards to accessing HID keyboards and mice. Try to target + // a specific interface first, if possible. + switch (LIBUSB_REQ_RECIPIENT(setup->RequestType)) { + case LIBUSB_RECIPIENT_INTERFACE: + iface = setup->Index & 0xFF; + break; + case LIBUSB_RECIPIENT_ENDPOINT: + r = libusb_get_active_config_descriptor(transfer->dev_handle->dev, &conf_desc); + if (r == LIBUSB_SUCCESS) { + iface = get_interface_by_endpoint(conf_desc, (setup->Index & 0xFF)); + libusb_free_config_descriptor(conf_desc); + break; + } + // No break if not able to determine interface + // Fall through + default: + iface = -1; + break; + } + + // Try and target a specific interface if the control setup indicates such + if ((iface >= 0) && (iface < USB_MAXINTERFACES)) { + 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); + if (r == LIBUSB_SUCCESS) + return r; + } + } + + // Either not targeted to a specific interface or no luck in doing so. + // Try a 2 pass approach with all interfaces. + for (pass = 0; pass < 2; pass++) { + for (iface = 0; iface < USB_MAXINTERFACES; iface++) { + 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(TRANSFER_CTX(transfer), "trying to skip restricted interface #%d (HID keyboard or mouse?)", iface); + continue; + } + 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) + continue; + return r; + } + } + } + + usbi_err(TRANSFER_CTX(transfer), "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +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 = get_winusb_device_handle_priv(transfer->dev_handle); + struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + int current_interface; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(TRANSFER_CTX(transfer), "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, submit_bulk_transfer); + + return priv->usb_interface[current_interface].apib-> + submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer); +} + +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct 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; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(TRANSFER_CTX(transfer), "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, submit_iso_transfer); + + return priv->usb_interface[current_interface].apib-> + submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer); +} + +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct 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; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, clear_halt); + + return priv->usb_interface[current_interface].apib-> + clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint); +} + +static int composite_cancel_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_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev); + int current_interface = transfer_priv->interface_number; + + UNUSED(sub_api); + + if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { + usbi_err(TRANSFER_CTX(transfer), "program assertion failed - invalid interface_number"); + return LIBUSB_ERROR_NOT_FOUND; + } + + CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, cancel_transfer); + + return priv->usb_interface[current_interface].apib-> + cancel_transfer(priv->usb_interface[current_interface].sub_api, itransfer); +} + +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); + int i, r; + bool available[SUB_API_MAX]; + + UNUSED(sub_api); + + for (i = 0; i < SUB_API_MAX; i++) + available[i] = false; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET)) + available[priv->usb_interface[i].sub_api] = true; + } + + for (i = 0; i < SUB_API_MAX; i++) { + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle); + if (r != LIBUSB_SUCCESS) + return r; + } + } + + return LIBUSB_SUCCESS; +} + +static enum libusb_transfer_status composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, DWORD length) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + 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; + + UNUSED(sub_api); + if (priv->usb_interface[current_interface].apib->copy_transfer_data == NULL) { + usbi_err(TRANSFER_CTX(transfer), "program assertion failed - no function to copy transfer data"); + return LIBUSB_TRANSFER_ERROR; + } + + return priv->usb_interface[current_interface].apib-> + copy_transfer_data(priv->usb_interface[current_interface].sub_api, itransfer, length); +} + +static int composite_endpoint_supports_raw_io(int sub_api, struct libusb_device_handle *dev_handle, + uint8_t endpoint) +{ + 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; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface, cannot query RAW_IO support"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (priv->usb_interface[current_interface].apib->endpoint_supports_raw_io == NULL) + { + usbi_dbg(HANDLE_CTX(dev_handle), "device driver doesn't support RAW_IO support query"); + return 0; + } + + return priv->usb_interface[current_interface].apib-> + endpoint_supports_raw_io(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint); +} + +static int composite_endpoint_set_raw_io(int sub_api, libusb_device_handle *dev_handle, + uint8_t endpoint, int enable) +{ + 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; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface, cannot query RAW_IO support"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (priv->usb_interface[current_interface].apib->endpoint_set_raw_io == NULL) + { + usbi_dbg(HANDLE_CTX(dev_handle), "device driver doesn't support setting RAW_IO"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return priv->usb_interface[current_interface].apib-> + endpoint_set_raw_io(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint, enable); +} + +static int composite_get_max_raw_io_transfer_size(int sub_api, + libusb_device_handle *dev_handle, + uint8_t endpoint) +{ + 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; + + UNUSED(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(HANDLE_CTX(dev_handle), "unable to match endpoint to an open interface - cannot get max RAW_IO transfer size"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (priv->usb_interface[current_interface].apib->get_max_raw_io_transfer_size == NULL) + { + usbi_dbg(HANDLE_CTX(dev_handle), "device driver doesn't support querying max RAW_IO transfer size"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return priv->usb_interface[current_interface].apib-> + get_max_raw_io_transfer_size(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.h new file mode 100644 index 00000000..832fd22e --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/os/windows_winusb.h @@ -0,0 +1,792 @@ +/* + * Windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_WINDOWS_WINUSB_H +#define LIBUSB_WINDOWS_WINUSB_H + +#include +#include +#include + +#include "windows_common.h" + +#define MAX_CTRL_BUFFER_LENGTH 4096 +#define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 +#define MAX_GUID_STRING_LENGTH 39 +#define MAX_PATH_LENGTH 256 +#define MAX_KEY_LENGTH 256 +#define LIST_SEPARATOR ';' + +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + +// libusb0 Filter Device Interface GUID +DEFINE_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 + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_HUB 1 +#define USB_API_COMPOSITE 2 +#define USB_API_WINUSBX 3 +#define USB_API_HID 4 +#define USB_API_MAX 5 + +// Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL) +// Must have the same values as the KUSB_DRVID enum from libusbk.h +#define SUB_API_NOTSET -1 +#define SUB_API_LIBUSBK 0 +#define SUB_API_LIBUSB0 1 +#define SUB_API_WINUSB 2 +#define SUB_API_MAX 3 + +struct windows_usb_api_backend { + const uint8_t id; + const char * const designation; + const char * const * const driver_name_list; // Driver name, without .sys, e.g. "usbccgp" + const uint8_t nb_driver_names; + bool (*init)(struct libusb_context *ctx); + void (*exit)(void); + int (*open)(int sub_api, struct libusb_device_handle *dev_handle); + void (*close)(int sub_api, struct libusb_device_handle *dev_handle); + int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); + int (*claim_interface)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); + int (*set_interface_altsetting)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting); + int (*release_interface)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface); + int (*clear_halt)(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); + int (*reset_device)(int sub_api, struct libusb_device_handle *dev_handle); + int (*submit_bulk_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_iso_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_control_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*cancel_transfer)(int sub_api, struct usbi_transfer *itransfer); + enum libusb_transfer_status (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, DWORD length); + int (*endpoint_supports_raw_io)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint); + int (*endpoint_set_raw_io)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint, int enable); + int (*get_max_raw_io_transfer_size)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint); +}; + +extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; + +#define PRINT_UNSUPPORTED_API(fname) \ + usbi_dbg(NULL, "unsupported API call for '%s' " \ + "(unrecognized device driver)", #fname) + +#define CHECK_SUPPORTED_API(apip, fname) \ + do { \ + if ((apip)->fname == NULL) { \ + PRINT_UNSUPPORTED_API(fname); \ + return LIBUSB_ERROR_NOT_SUPPORTED; \ + } \ + } while (0) + +/* + * private structures definition + * with inline pseudo constructors/destructors + */ + +// TODO (v2+): move hid desc to libusb.h? +struct libusb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bClassDescriptorType; + uint16_t wClassDescriptorLength; +}; + +#define LIBUSB_DT_HID_SIZE 9 +#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \ + + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE) +#define HID_MAX_REPORT_SIZE 1024 +#define HID_IN_EP 0x81 +#define HID_OUT_EP 0x02 +#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F) +#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5)) +#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN) +#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) + +// The following are used for HID reports IOCTLs +#define HID_IN_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + +#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) +#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) +#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100) +#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101) + +enum libusb_hid_request_type { + HID_REQ_GET_REPORT = 0x01, + HID_REQ_GET_IDLE = 0x02, + HID_REQ_GET_PROTOCOL = 0x03, + HID_REQ_SET_REPORT = 0x09, + HID_REQ_SET_IDLE = 0x0A, + HID_REQ_SET_PROTOCOL = 0x0B +}; + +enum libusb_hid_report_type { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03 +}; + +struct hid_device_priv { + uint16_t vid; + uint16_t pid; + uint8_t config; + uint8_t nb_interfaces; + bool uses_report_ids[3]; // input, ouptput, feature + uint16_t input_report_size; + uint16_t output_report_size; + uint16_t feature_report_size; + uint16_t usage; + uint16_t usagePage; + WCHAR string[3][MAX_USB_STRING_LENGTH]; + uint8_t string_index[3]; // man, prod, ser +}; + +static inline struct winusb_device_priv *winusb_device_priv_init(struct libusb_device *dev) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + int i; + + usbi_mutex_init(&priv->interface_lock); + priv->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + priv->sub_api = SUB_API_NOTSET; + for (i = 0; i < USB_MAXINTERFACES; i++) { + priv->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + priv->usb_interface[i].sub_api = SUB_API_NOTSET; + } + + return priv; +} + +static inline void winusb_device_priv_release(struct libusb_device *dev) +{ + struct winusb_device_priv *priv = usbi_get_device_priv(dev); + int i; + + free(priv->dev_id); + free(priv->path); + if ((dev->device_descriptor.bNumConfigurations > 0) && (priv->config_descriptor != NULL)) { + for (i = 0; i < dev->device_descriptor.bNumConfigurations; i++) { + if (priv->config_descriptor[i] == NULL) + continue; + free((UCHAR *)priv->config_descriptor[i] - USB_DESCRIPTOR_REQUEST_SIZE); + } + } + free(priv->config_descriptor); + free(priv->hid); + for (i = 0; i < USB_MAXINTERFACES; i++) { + free(priv->usb_interface[i].path); + free(priv->usb_interface[i].endpoint); + } + usbi_mutex_destroy(&priv->interface_lock); +} + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retrieve list + const char *designation; // internal designation (for debug output) +}; + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 + +/* Cfgmgr32 dependencies */ +DLL_DECLARE_HANDLE(Cfgmgr32); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); + +/* AdvAPI32 dependencies */ +DLL_DECLARE_HANDLE(AdvAPI32); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExA, (HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* SetupAPI dependencies */ +DLL_DECLARE_HANDLE(SetupAPI); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (LPCGUID, PCSTR, HWND, DWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + LPCGUID, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInstanceIdA, (HDEVINFO, PSP_DEVINFO_DATA, + PCSTR, DWORD, PDWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +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 + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 + +#define USB_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + USB_CTL_CODE(USB_GET_NODE_INFORMATION) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + USB_CTL_CODE(USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ + USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2) + +typedef enum _USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub, + DeviceEnumerating, + DeviceReset +} USB_CONNECTION_STATUS; + +typedef enum _USB_DEVICE_SPEED { + UsbLowSpeed = 0, + UsbFullSpeed, + UsbHighSpeed, + UsbSuperSpeed, + UsbSuperSpeedPlus // Not in Microsoft headers +} USB_DEVICE_SPEED; + +typedef enum _USB_HUB_NODE { + UsbHub, + 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 +#pragma pack(push, 1) + +typedef struct _USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct _USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct _USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; +// USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct _USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct _USB_CONFIGURATION_DESCRIPTOR_SHORT { + USB_DESCRIPTOR_REQUEST req; + USB_CONFIGURATION_DESCRIPTOR desc; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef union _USB_PROTOCOLS { + ULONG ul; + struct { + ULONG Usb110:1; + ULONG Usb200:1; + ULONG Usb300:1; + ULONG ReservedMBZ:29; + }; +} USB_PROTOCOLS, *PUSB_PROTOCOLS; + +typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS { + ULONG ul; + struct { + ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1; + ULONG DeviceIsSuperSpeedCapableOrHigher:1; + ULONG DeviceIsOperatingAtSuperSpeedPlusOrHigher:1; + ULONG DeviceIsSuperSpeedPlusCapableOrHigher:1; + ULONG ReservedMBZ:28; + }; +} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS; + +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { + ULONG ConnectionIndex; + ULONG Length; + USB_PROTOCOLS SupportedUsbProtocols; + USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; +} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; + +#pragma pack(pop) + +#if defined(_MSC_VER) +// Restore original warnings +#pragma warning(pop) +#endif + +/* winusb.dll interface */ + +/* pipe policies */ +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +/* libusbK */ +#define ISO_ALWAYS_START_ASAP 0x21 + +typedef struct _USBD_ISO_PACKET_DESCRIPTOR { + ULONG Offset; + ULONG Length; + USBD_STATUS Status; +} USBD_ISO_PACKET_DESCRIPTOR, *PUSBD_ISO_PACKET_DESCRIPTOR; + +typedef enum _USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; + ULONG MaximumBytesPerInterval; +} WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX; + +#pragma pack(push, 1) + +typedef struct _WINUSB_SETUP_PACKET { + UCHAR RequestType; + UCHAR Request; + USHORT Value; + USHORT Index; + USHORT Length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; + +#pragma pack(pop) + +typedef PVOID WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; +typedef PVOID WINUSB_ISOCH_BUFFER_HANDLE, *PWINUSB_ISOCH_BUFFER_HANDLE; + +typedef BOOL (WINAPI *WinUsb_AbortPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + WINUSB_SETUP_PACKET SetupPacket, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_FlushPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_Free_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AssociatedInterfaceIndex, + PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_Initialize_t)( + HANDLE DeviceHandle, + PWINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_QueryPipeEx_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceHandle, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION_EX PipeInformationEx +); +typedef BOOL (WINAPI *WinUsb_ReadIsochPipeAsap_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + ULONG NumberOfPackets, + PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ReadPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_RegisterIsochBuffer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PVOID Buffer, + ULONG BufferLength, + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle +); +typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_UnregisterIsochBuffer_t)( + WINUSB_ISOCH_BUFFER_HANDLE BufferHandle +); +typedef BOOL (WINAPI *WinUsb_WriteIsochPipeAsap_t)( + WINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_WritePipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); + +/* /!\ These must match the ones from the official libusbk.h */ +typedef enum _KUSB_FNID { + KUSB_FNID_Init, + KUSB_FNID_Free, + KUSB_FNID_ClaimInterface, + KUSB_FNID_ReleaseInterface, + KUSB_FNID_SetAltInterface, + KUSB_FNID_GetAltInterface, + KUSB_FNID_GetDescriptor, + KUSB_FNID_ControlTransfer, + KUSB_FNID_SetPowerPolicy, + KUSB_FNID_GetPowerPolicy, + KUSB_FNID_SetConfiguration, + KUSB_FNID_GetConfiguration, + KUSB_FNID_ResetDevice, + KUSB_FNID_Initialize, + KUSB_FNID_SelectInterface, + KUSB_FNID_GetAssociatedInterface, + KUSB_FNID_Clone, + KUSB_FNID_QueryInterfaceSettings, + KUSB_FNID_QueryDeviceInformation, + KUSB_FNID_SetCurrentAlternateSetting, + KUSB_FNID_GetCurrentAlternateSetting, + KUSB_FNID_QueryPipe, + KUSB_FNID_SetPipePolicy, + KUSB_FNID_GetPipePolicy, + KUSB_FNID_ReadPipe, + KUSB_FNID_WritePipe, + KUSB_FNID_ResetPipe, + KUSB_FNID_AbortPipe, + KUSB_FNID_FlushPipe, + KUSB_FNID_IsoReadPipe, + KUSB_FNID_IsoWritePipe, + KUSB_FNID_GetCurrentFrameNumber, + KUSB_FNID_GetOverlappedResult, + KUSB_FNID_GetProperty, + KUSB_FNID_COUNT, +} KUSB_FNID; + +typedef struct _KLIB_VERSION { + INT Major; + INT Minor; + INT Micro; + INT Nano; +} KLIB_VERSION, *PKLIB_VERSION; + +typedef BOOL (WINAPI *LibK_GetProcAddress_t)( + PVOID ProcAddress, + INT DriverID, + INT FunctionID +); + +typedef VOID (WINAPI *LibK_GetVersion_t)( + PKLIB_VERSION Version +); + +typedef BOOL (WINAPI *LibK_ResetDevice_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); + +//KISO_PACKET is equivalent of libusb_iso_packet_descriptor except uses absolute "offset" field instead of sequential Lengths +typedef struct _KISO_PACKET { + UINT offset; + USHORT actual_length; //changed from libusbk_shared.h "Length" for clarity + USHORT status; +} KISO_PACKET, *PKISO_PACKET; + +typedef enum _KISO_FLAG { + KISO_FLAG_NONE = 0, + KISO_FLAG_SET_START_FRAME = 0x00000001, +} KISO_FLAG; + +//KISO_CONTEXT is the conceptual equivalent of libusb_transfer except is isochronous-specific and must match libusbk's version +typedef struct _KISO_CONTEXT { + KISO_FLAG Flags; + UINT StartFrame; + SHORT ErrorCount; + SHORT NumberOfPackets; + UINT UrbHdrStatus; + KISO_PACKET IsoPackets[0]; +} KISO_CONTEXT, *PKISO_CONTEXT; + +typedef BOOL(WINAPI *LibK_IsoReadPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + LPOVERLAPPED Overlapped, + PKISO_CONTEXT IsoContext +); + +typedef BOOL(WINAPI *LibK_IsoWritePipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + LPOVERLAPPED Overlapped, + PKISO_CONTEXT IsoContext +); + +struct winusb_interface { + HMODULE hDll; + WinUsb_AbortPipe_t AbortPipe; + WinUsb_ControlTransfer_t ControlTransfer; + WinUsb_FlushPipe_t FlushPipe; + WinUsb_Free_t Free; + WinUsb_GetAssociatedInterface_t GetAssociatedInterface; + WinUsb_Initialize_t Initialize; + WinUsb_ReadPipe_t ReadPipe; + WinUsb_ResetPipe_t ResetPipe; + WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; + WinUsb_SetPipePolicy_t SetPipePolicy; + WinUsb_GetPipePolicy_t GetPipePolicy; + WinUsb_WritePipe_t WritePipe; + union { + struct { + // Isochoronous functions for libusbK sub api: + LibK_IsoReadPipe_t IsoReadPipe; + LibK_IsoWritePipe_t IsoWritePipe; + // Reset device function for libusbK sub api: + LibK_ResetDevice_t ResetDevice; + }; + struct { + // Isochronous functions for WinUSB sub api: + WinUsb_QueryPipeEx_t QueryPipeEx; + WinUsb_ReadIsochPipeAsap_t ReadIsochPipeAsap; + WinUsb_RegisterIsochBuffer_t RegisterIsochBuffer; + WinUsb_UnregisterIsochBuffer_t UnregisterIsochBuffer; + WinUsb_WriteIsochPipeAsap_t WriteIsochPipeAsap; + }; + }; +}; + +/* hid.dll interface */ + +#define HIDP_STATUS_SUCCESS 0x110000 +typedef void * PHIDP_PREPARSED_DATA; + +#pragma pack(push, 1) + +typedef struct _HIDD_ATTIRBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + +#pragma pack(pop) + +typedef USHORT USAGE; +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + } u; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +DLL_DECLARE_HANDLE(hid); +DLL_DECLARE_FUNC(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetIndexedString, (HANDLE, ULONG, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); + +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/strerror.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/strerror.c new file mode 100644 index 00000000..eb355034 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/strerror.c @@ -0,0 +1,223 @@ +/* + * libusb strerror code + * Copyright © 2013 Hans de Goede + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include +#include + +/** \ingroup libusb_misc + * How to add a new \ref libusb_strerror() translation: + *
    + *
  1. Download the latest \c strerror.c from:
    + * https://raw.github.com/libusb/libusb/master/libusb/strerror.c
  2. + *
  3. Open the file in an UTF-8 capable editor
  4. + *
  5. Add the 2 letter ISO 639-1 + * code for your locale at the end of \c usbi_locale_supported[]
    + * Eg. for Chinese, you would add "zh" so that: + * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode + * becomes: + * \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode
  6. + *
  7. Copy the { / * English (en) * / ... } section and add it at the end of \c usbi_localized_errors
    + * Eg. for Chinese, the last section of \c usbi_localized_errors could look like: + * \code + * }, { / * Chinese (zh) * / + * "Success", + * ... + * "Other error", + * }, + * };\endcode
  8. + *
  9. Translate each of the English messages from the section you copied into your language
  10. + *
  11. Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net
  12. + *
+ */ + +static const char * const usbi_locale_supported[] = { "en", "nl", "fr", "ru", "de", "hu" }; +static const char * const usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { + { /* English (en) */ + "Success", + "Input/Output Error", + "Invalid parameter", + "Access denied (insufficient permissions)", + "No such device (it may have been disconnected)", + "Entity not found", + "Resource busy", + "Operation timed out", + "Overflow", + "Pipe error", + "System call interrupted (perhaps due to signal)", + "Insufficient memory", + "Operation not supported or unimplemented on this platform", + "Other error", + }, { /* Dutch (nl) */ + "Gelukt", + "Invoer-/uitvoerfout", + "Ongeldig argument", + "Toegang geweigerd (onvoldoende toegangsrechten)", + "Apparaat bestaat niet (verbinding met apparaat verbroken?)", + "Niet gevonden", + "Apparaat of hulpbron is bezig", + "Bewerking verlopen", + "Waarde is te groot", + "Gebroken pijp", + "Onderbroken systeemaanroep", + "Onvoldoende geheugen beschikbaar", + "Bewerking wordt niet ondersteund", + "Andere fout", + }, { /* French (fr) */ + "Succès", + "Erreur d'entrée/sortie", + "Paramètre invalide", + "Accès refusé (permissions insuffisantes)", + "Périphérique introuvable (peut-être déconnecté)", + "Elément introuvable", + "Resource déjà occupée", + "Operation expirée", + "Débordement", + "Erreur de pipe", + "Appel système abandonné (peut-être à cause d’un signal)", + "Mémoire insuffisante", + "Opération non supportée or non implémentée sur cette plateforme", + "Autre erreur", + }, { /* Russian (ru) */ + "Успех", + "Ошибка ввода/вывода", + "Неверный параметр", + "Доступ запрещён (не хватает прав)", + "Устройство отсутствует (возможно, оно было отсоединено)", + "Элемент не найден", + "Ресурс занят", + "Истекло время ожидания операции", + "Переполнение", + "Ошибка канала", + "Системный вызов прерван (возможно, сигналом)", + "Память исчерпана", + "Операция не поддерживается данной платформой", + "Неизвестная ошибка" + }, { /* German (de) */ + "Erfolgreich", + "Eingabe-/Ausgabefehler", + "Ungültiger Parameter", + "Keine Berechtigung (Zugriffsrechte fehlen)", + "Kein passendes Gerät gefunden (es könnte entfernt worden sein)", + "Entität nicht gefunden", + "Die Ressource ist belegt", + "Die Wartezeit für die Operation ist abgelaufen", + "Mehr Daten empfangen als erwartet", + "Datenübergabe unterbrochen (broken pipe)", + "Unterbrechung während des Betriebssystemaufrufs", + "Nicht genügend Hauptspeicher verfügbar", + "Die Operation wird nicht unterstützt oder ist auf dieser Platform nicht implementiert", + "Allgemeiner Fehler", + }, { /* Hungarian (hu) */ + "Sikeres", + "Be-/kimeneti hiba", + "Érvénytelen paraméter", + "Hozzáférés megtagadva", + "Az eszköz nem található (eltávolították?)", + "Nem található", + "Az erőforrás foglalt", + "Időtúllépés", + "Túlcsordulás", + "Törött adatcsatorna", + "Rendszerhívás megszakítva", + "Nincs elég memória", + "A művelet nem támogatott ezen a rendszeren", + "Általános hiba", + }, +}; + +static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_localized_errors[0]; + +/** \ingroup libusb_misc + * Set the language, and only the language, not the encoding! used for + * translatable libusb messages. + * + * This takes a locale string in the default setlocale format: lang[-region] + * or lang[_country_region][.codeset]. Only the lang part of the string is + * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". + * The optional region, country_region or codeset parts are ignored. This + * means that functions which return translatable strings will NOT honor the + * specified encoding. + * All strings returned are encoded as UTF-8 strings. + * + * If libusb_setlocale() is not called, all messages will be in English. + * + * The following functions return translatable strings: libusb_strerror(). + * Note that the libusb log messages controlled through LIBUSB_OPTION_LOG_LEVEL + * are not translated, they are always in English. + * + * For POSIX UTF-8 environments if you want libusb to follow the standard + * locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)), + * after your app has done its locale setup. + * + * \param locale locale-string in the form of lang[_country_region][.codeset] + * or lang[-region], where lang is a 2 letter ISO 639-1 code + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns a LIBUSB_ERROR code on other errors + */ + +int API_EXPORTED libusb_setlocale(const char *locale) +{ + size_t i; + + if (!locale || strlen(locale) < 2 + || (locale[2] != '\0' && locale[2] != '-' && locale[2] != '_' && locale[2] != '.')) + return LIBUSB_ERROR_INVALID_PARAM; + + for (i = 0; i < ARRAYSIZE(usbi_locale_supported); i++) { + if (usbi_locale_supported[i][0] == tolower((unsigned char)locale[0]) + && usbi_locale_supported[i][1] == tolower((unsigned char)locale[1])) + break; + } + + if (i == ARRAYSIZE(usbi_locale_supported)) + return LIBUSB_ERROR_NOT_FOUND; + + usbi_error_strings = &usbi_localized_errors[i]; + + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_misc + * Returns a constant string with a short description of the given error code, + * this description is intended for displaying to the end user and will be in + * the language set by libusb_setlocale(). + * + * The returned string is encoded in UTF-8. + * + * The messages always start with a capital letter and end without any dot. + * The caller must not free() the returned string. + * + * \param errcode the error code whose description is desired + * \returns a short description of the error code in UTF-8 encoding + */ +DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_strerror(int errcode) +{ + int errcode_index = -errcode; + + if (errcode_index < 0 || errcode_index >= LIBUSB_ERROR_COUNT) { + /* "Other Error", which should always be our last message, is returned */ + errcode_index = LIBUSB_ERROR_COUNT - 1; + } + + return (*usbi_error_strings)[errcode_index]; +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/sync.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/sync.c new file mode 100644 index 00000000..733927d9 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/sync.c @@ -0,0 +1,342 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Synchronous I/O functions for libusb + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2019 Nathan Hjelm + * Copyright © 2019 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include +#include + +/** + * @defgroup libusb_syncio Synchronous device I/O + * + * This page documents libusb's synchronous (blocking) API for USB device I/O. + * This interface is easy to use but has some limitations. More advanced users + * may wish to consider using the \ref libusb_asyncio "asynchronous I/O API" instead. + */ + +static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) +{ + usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); + + int *completed = transfer->user_data; + *completed = 1; + /* + * Right after setting 'completed', another thread might free the transfer, so don't + * access it beyond this point. The instantiating thread (not necessarily the + * current one) interprets the result and frees the transfer. + */ +} + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle); + + while (!*completed) { + r = libusb_handle_events_completed(ctx, completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying", + libusb_error_name(r)); + libusb_cancel_transfer(transfer); + continue; + } + if (NULL == transfer->dev_handle) { + /* transfer completion after libusb_close() */ + transfer->status = LIBUSB_TRANSFER_NO_DEVICE; + *completed = 1; + } + } +} + +/** \ingroup libusb_syncio + * Perform a USB control transfer. + * + * The direction of the transfer is inferred from the bmRequestType field of + * the setup packet. + * + * The wValue, wIndex and wLength fields values should be given in host-endian + * byte order. + * + * \param dev_handle a handle for the device to communicate with + * \param bmRequestType the request type field for the setup packet + * \param bRequest the request field for the setup packet + * \param wValue the value field for the setup packet + * \param wIndex the index field for the setup packet + * \param data a suitably-sized data buffer for either input or output + * (depending on direction bits within bmRequestType) + * \param wLength the length field for the setup packet. The data buffer should + * be at least this size. + * \param timeout timeout (in milliseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * \returns on success, the number of bytes actually transferred + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the control request was not supported by the + * device + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) +{ + struct libusb_transfer *transfer; + unsigned char *buffer; + int completed = 0; + int r; + + if (usbi_handling_events(HANDLE_CTX(dev_handle))) + return LIBUSB_ERROR_BUSY; + + transfer = libusb_alloc_transfer(0); + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + if (!buffer) { + libusb_free_transfer(transfer); + return LIBUSB_ERROR_NO_MEM; + } + + libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, + wLength); + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, + sync_transfer_cb, &completed, timeout); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, libusb_control_transfer_get_data(transfer), + (size_t)transfer->actual_length); + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = transfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + int *transferred, unsigned int timeout, unsigned char type) +{ + struct libusb_transfer *transfer; + int completed = 0; + int r; + + if (usbi_handling_events(HANDLE_CTX(dev_handle))) + return LIBUSB_ERROR_BUSY; + + transfer = libusb_alloc_transfer(0); + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, + sync_transfer_cb, &completed, timeout); + transfer->type = type; + + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if (transferred) { + assert(transfer->actual_length >= 0); + *transferred = transfer->actual_length; + } + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +/** \ingroup libusb_syncio + * Perform a USB bulk transfer. The direction of the transfer is inferred from + * the direction bits of the endpoint address. + * + * For bulk reads, the length field indicates the maximum length of + * data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for bulk writes. + * Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. See \ref asynctimeout for more details. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), + * it is legal to pass a NULL pointer if you do not wish to receive this + * information. + * \param timeout timeout (in milliseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * transferred) + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref libusb_packetoverflow + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); +} + +/** \ingroup libusb_syncio + * Perform a USB interrupt transfer. The direction of the transfer is inferred + * from the direction bits of the endpoint address. + * + * For interrupt reads, the length field indicates the maximum length + * of data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for interrupt + * writes. Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. See \ref asynctimeout for more details. + * + * The default endpoint bInterval value is used as the polling interval. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. Will never be negative. Since version 1.0.21 + * (\ref LIBUSB_API_VERSION >= 0x01000105), it is legal to pass a NULL + * pointer if you do not wish to receive this information. + * \param timeout timeout (in milliseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref libusb_packetoverflow + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) + * \returns another LIBUSB_ERROR code on other error + */ +int API_EXPORTED libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version.h new file mode 100644 index 00000000..2a926583 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#include "version_nano.h" +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 30 +#endif +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version_nano.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version_nano.h new file mode 100644 index 00000000..f009126a --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/libusb/version_nano.h @@ -0,0 +1 @@ +#define LIBUSB_NANO 12037 diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Base.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Base.props new file mode 100644 index 00000000..ddc0f2a2 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Base.props @@ -0,0 +1,60 @@ + + + + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..'))\ + $(LibusbRootDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\$(ProjectName)\ + $(IntDir)..\ + + + + + .;..\libusb;%(AdditionalIncludeDirectories) + _WIN32_WINNT=_WIN32_WINNT_VISTA;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + Level4 + true + true + true + + /utf-8 %(AdditionalOptions) + ProgramDatabase + Caret + + + + Disabled + + + Default + + + + NDEBUG;%(PreprocessorDefinitions) + AnySuitable + Speed + true + true + /Gw %(AdditionalOptions) + true + + + + true + true + + + + UseLinkTimeCodeGeneration + true + true + kernel32.lib;%(AdditionalDependencies) + + + + Console + + + true + true + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Application.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Application.props new file mode 100644 index 00000000..420d1140 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Application.props @@ -0,0 +1,7 @@ + + + + + Application + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Base.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Base.props new file mode 100644 index 00000000..67372a0c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.Base.props @@ -0,0 +1,48 @@ + + + + + v120 + v140 + v141 + v142 + v143 + v145 + + $(PlatformToolset.Substring(1)) + Unicode + x64 + + + + + + MultiThreadedDebug + + + MultiThreaded + + + + + true + true + + 10.0.19041.0 + $(WindowsSDKVersion) + + + 10.0 + + diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.DynamicLibrary.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.DynamicLibrary.props new file mode 100644 index 00000000..9685fe11 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.DynamicLibrary.props @@ -0,0 +1,33 @@ + + + + + DynamicLibrary + + + + MultiThreadedDebugDLL + + + MultiThreadedDebugDLL + + + MultiThreadedDebug + + + MultiThreadedDebug + + + MultiThreadedDLL + + + MultiThreadedDLL + + + MultiThreaded + + + MultiThreaded + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.StaticLibrary.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.StaticLibrary.props new file mode 100644 index 00000000..44fc9d56 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/Configuration.StaticLibrary.props @@ -0,0 +1,7 @@ + + + + + StaticLibrary + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/ProjectConfigurations.Base.props b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/ProjectConfigurations.Base.props new file mode 100644 index 00000000..2f45f7ca --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/ProjectConfigurations.Base.props @@ -0,0 +1,122 @@ + + + + + + + + + LIBUSB_WINDOWS_HOTPLUG;%(PreprocessorDefinitions) + + + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + Debug-MT + ARM64 + + + Debug-MT + Win32 + + + Debug-MT + x64 + + + Release-MT + ARM64 + + + Release-MT + Win32 + + + Release-MT + x64 + + + Debug-Hotplug + ARM64 + + + Debug-Hotplug + Win32 + + + Debug-Hotplug + x64 + + + Release-Hotplug + ARM64 + + + Release-Hotplug + Win32 + + + Release-Hotplug + x64 + + + Debug-Hotplug-MT + ARM64 + + + Debug-Hotplug-MT + Win32 + + + Debug-Hotplug-MT + x64 + + + Release-Hotplug-MT + ARM64 + + + Release-Hotplug-MT + Win32 + + + Release-Hotplug-MT + x64 + + + + true + + + true + + + true + + + true + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/build_all.ps1 b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/build_all.ps1 new file mode 100644 index 00000000..0a5f9bdc --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/build_all.ps1 @@ -0,0 +1,17 @@ +$toolsets = "v120", "v140", "v141", "v142", "v143", "v145" +$platforms = "Win32", "x64", "ARM", "ARM64" +$configurations = "Debug", "Release" + +foreach ($toolset in $toolsets) { + foreach ($plat in $platforms) { + if (("v120", "v140").contains($toolset) -and $plat -eq "ARM64") { + # VS2013,VS2015 don't support arm64 + write-host ">>> PlatformToolset=$toolset,Platform=$plat SKIP" + continue + } + foreach ($conf in $configurations) { + write-host ">>> PlatformToolset=$toolset,Platform=$plat,Configuration=$conf" + msbuild -m -v:m -p:PlatformToolset=$toolset,Platform=$plat,Configuration=$conf $PSScriptRoot\libusb.sln + } + } +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/config.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/config.h new file mode 100644 index 00000000..3d7dbaa1 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/config.h @@ -0,0 +1,62 @@ +/* config.h. Manual config for MSVC. */ + +#ifndef _MSC_VER +#warn "msvc/config.h shouldn't be included for your development environment." +#error "Please make sure the msvc/ directory is removed from your build path." +#endif + +/* Visual Studio 2013 or later is required */ +#if (_MSC_VER < 1800) +#error "Visual Studio 2013 or later is required." +#endif + +/* Visual Studio 2013 and 2015 do not support __func__ */ +#if (_MSC_VER <= 1900) +#define __func__ __FUNCTION__ +#endif + +/* Visual Studio 2015 and later defines timespec */ +#if (_MSC_VER >= 1900) +#define _TIMESPEC_DEFINED 1 +#endif + +/* Disable: warning C4127: conditional expression is constant */ +#pragma warning(disable:4127) +/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */ +#pragma warning(disable:4200) +/* Disable: warning C4201: nonstandard extension used : nameless struct/union */ +#pragma warning(disable:4201) +/* Disable: warning C4324: structure was padded due to __declspec(align()) */ +#pragma warning(disable:4324) +/* Disable: warning C4996: 'GetVersionExA': was declared deprecated */ +#pragma warning(disable:4996) +#if (_MSC_VER > 1800) +/* Disable: warning C5287: operands are different enum types, supported after Visual Studio 2013 */ +#pragma warning(disable:5287) +#endif + +#if defined(_PREFAST_) +/* Disable "Banned API" errors when using the MS's WDK OACR/Prefast */ +#pragma warning(disable:28719) +/* Disable "The function 'InitializeCriticalSection' must be called from within a try/except block" */ +#pragma warning(disable:28125) +#endif + +/* Define to the attribute for default visibility. */ +#define DEFAULT_VISIBILITY /**/ + +/* Define to 1 to start with debug message logging enabled. */ +/* #undef ENABLE_DEBUG_LOGGING */ + +/* Define to 1 to enable message logging. */ +#define ENABLE_LOGGING 1 + +/* Define to 1 if compiling for a Windows platform. */ +#define PLATFORM_WINDOWS 1 + +/* Define to the attribute for enabling parameter checks on printf-like + functions. */ +#define PRINTF_FORMAT(a, b) /**/ + +/* Define to 1 to output logging messages to the systemwide log. */ +/* #undef USE_SYSTEM_LOGGING_FACILITY */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp.vcxproj new file mode 100644 index 00000000..bf5eb47f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp.vcxproj @@ -0,0 +1,33 @@ + + + + + {8c7814a1-fd6e-4185-9ea0-8208119756d4} + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp_threaded.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp_threaded.vcxproj new file mode 100644 index 00000000..bab13c73 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/dpfp_threaded.vcxproj @@ -0,0 +1,38 @@ + + + + + {8c7814a2-fd6e-4185-9ea0-8208119756d4} + + + + + + + + + + + + + + DPFP_THREADED;%(PreprocessorDefinitions) + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/fxload.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/fxload.vcxproj new file mode 100644 index 00000000..44601478 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/fxload.vcxproj @@ -0,0 +1,46 @@ + + + + + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88} + + + + + + + + + + + + + + .\getopt;%(AdditionalIncludeDirectories) + __GNU_LIBRARY__;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + {ae83e1b4-ce06-47ee-b7a3-c3a1d7c2d71e} + false + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt.vcxproj new file mode 100644 index 00000000..66562cfd --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt.vcxproj @@ -0,0 +1,40 @@ + + + + + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E} + + + + + + + + + + + + + + .\getopt;%(AdditionalIncludeDirectories) + _GL_UNUSED=;HAVE_STRING_H;%(PreprocessorDefinitions) + 4100;4131;4702;%(DisableSpecificWarnings) + + + + + + + + + + + + + + + + + + + diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_core.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_core.h new file mode 100644 index 00000000..3909d37f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_core.h @@ -0,0 +1,96 @@ +/* Declarations for getopt (basic, portable features only). + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_CORE_H +#define _GETOPT_CORE_H 1 + +/* This header should not be used directly; include getopt.h or + unistd.h instead. Unlike most bits headers, it does not have + a protective #error, because the guard macro for getopt.h in + gnulib is not fixed. */ + +__BEGIN_DECLS + +/* For communication from 'getopt' to the caller. + When 'getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when 'ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to 'getopt'. + + On entry to 'getopt', zero means this is the first call; initialize. + + When 'getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, 'optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message 'getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, 'optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in 'optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU 'getopt'. + + The argument '--' causes premature termination of argument + scanning, explicitly telling 'getopt' that there are no more + options. + + If OPTS begins with '-', then non-option arguments are treated as + arguments to the option '\1'. This behavior is specific to the GNU + 'getopt'. If OPTS begins with '+', or POSIXLY_CORRECT is set in + the environment, then do not permute arguments. + + For standards compliance, the 'argv' argument has the type + char *const *, but this is inaccurate; if argument permutation is + enabled, the argv array (not the strings it points to) must be + writable. */ + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW __nonnull ((2, 3)); + +__END_DECLS + +#endif /* getopt_core.h */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_ext.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_ext.h new file mode 100644 index 00000000..94ca758f --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/bits/getopt_ext.h @@ -0,0 +1,77 @@ +/* Declarations for getopt (GNU extensions). + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_EXT_H +#define _GETOPT_EXT_H 1 + +/* This header should not be used directly; include getopt.h instead. + Unlike most bits headers, it does not have a protective #error, + because the guard macro for getopt.h in gnulib is not fixed. */ + +__BEGIN_DECLS + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of 'struct option' terminated by an element containing a name which is + zero. + + The field 'has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field 'flag' is not NULL, it points to a variable that is set + to the value given in the field 'val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an 'int' to + a compiled-in constant, such as set a value from 'optarg', set the + option's 'flag' field to zero and its 'val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero 'flag' field, 'getopt' + returns the contents of the 'val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the 'has_arg' field of 'struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW __nonnull ((2, 3)); +extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW __nonnull ((2, 3)); + +__END_DECLS + +#endif /* getopt_ext.h */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/features.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/features.h new file mode 100644 index 00000000..0907d0d0 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/features.h @@ -0,0 +1,21 @@ +/* Minimal features.h shim for standalone getopt build on MSVC. */ +#ifndef _FEATURES_H +#define _FEATURES_H 1 + +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS +# define __END_DECLS +#endif + +#ifndef __THROW +# define __THROW +#endif + +#ifndef __nonnull +# define __nonnull(params) +#endif + +#endif /* _FEATURES_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.c new file mode 100644 index 00000000..b418830c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.c @@ -0,0 +1,811 @@ +/* Getopt for GNU. + Copyright (C) 1987-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _LIBC +# include +#endif + +#include "getopt.h" + +#include +#include +#include +#include + +#ifdef _LIBC +/* When used as part of glibc, error printing must be done differently + for standards compliance. getopt is not a cancellation point, so + it must not call functions that are, and it is specified by an + older standard than stdio locking, so it must not refer to + functions in the "user namespace" related to stdio locking. + Finally, it must use glibc's internal message translation so that + the messages are looked up in the proper text domain. */ +# include +# define fprintf __fxprintf_nocancel +# define flockfile(fp) _IO_flockfile (fp) +# define funlockfile(fp) _IO_funlockfile (fp) +#else +# include "gettext.h" +# define _(msgid) gettext (msgid) +/* When used standalone, flockfile and funlockfile might not be + available. */ +# if (!defined _POSIX_THREAD_SAFE_FUNCTIONS \ + || (defined _WIN32 && ! defined __CYGWIN__)) +# define flockfile(fp) /* nop */ +# define funlockfile(fp) /* nop */ +# endif +/* When used standalone, do not attempt to use alloca. */ +# define __libc_use_alloca(size) 0 +# undef alloca +# define alloca(size) (abort (), (void *)0) +#endif + +/* This implementation of 'getopt' has three modes for handling + options interspersed with non-option arguments. It can stop + scanning for options at the first non-option argument encountered, + as POSIX specifies. It can continue scanning for options after the + first non-option argument, but permute 'argv' as it goes so that, + after 'getopt' is done, all the options precede all the non-option + arguments and 'optind' points to the first non-option argument. + Or, it can report non-option arguments as if they were arguments to + the option character '\x01'. + + The default behavior of 'getopt_long' is to permute the argument list. + When this implementation is used standalone, the default behavior of + 'getopt' is to stop at the first non-option argument, but when it is + used as part of GNU libc it also permutes the argument list. In both + cases, setting the environment variable POSIXLY_CORRECT to any value + disables permutation. + + If the first character of the OPTSTRING argument to 'getopt' or + 'getopt_long' is '+', both functions will stop at the first + non-option argument. If it is '-', both functions will report + non-option arguments as arguments to the option character '\x01'. */ + +#include "getopt_int.h" + +/* For communication from 'getopt' to the caller. + When 'getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when 'ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to 'getopt'. + + On entry to 'getopt', zero means this is the first call; initialize. + + When 'getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, 'optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Keep a global copy of all internal members of getopt_data. */ + +static struct _getopt_data getopt_data; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + 'first_nonopt' and 'last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv, struct _getopt_data *d) +{ + int bottom = d->__first_nonopt; + int middle = d->__last_nonopt; + int top = d->optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + d->__first_nonopt += (d->optind - d->__last_nonopt); + d->__last_nonopt = d->optind; +} + +/* Process the argument starting with d->__nextchar as a long option. + d->optind should *not* have been advanced over this argument. + + If the value returned is -1, it was not actually a long option, the + state is unchanged, and the argument should be processed as a set + of short options (this can only happen when long_only is true). + Otherwise, the option (and its argument, if any) have been consumed + and the return value is the value to return from _getopt_internal_r. */ +static int +process_long_option (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, struct _getopt_data *d, + int print_errors, const char *prefix) +{ + char *nameend; + size_t namelen; + const struct option *p; + const struct option *pfound = NULL; + int n_options; + int option_index = 0; + + for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + namelen = nameend - d->__nextchar; + + /* First look for an exact match, counting the options as a side + effect. */ + for (p = longopts, n_options = 0; p->name; p++, n_options++) + if (!strncmp (p->name, d->__nextchar, namelen) + && namelen == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + option_index = n_options; + break; + } + + if (pfound == NULL) + { + /* Didn't find an exact match, so look for abbreviations. */ + unsigned char *ambig_set = NULL; + int ambig_malloced = 0; + int ambig_fallback = 0; + int indfound = -1; + + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, namelen)) + { + if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + { + /* Second or later nonexact match found. */ + if (!ambig_fallback) + { + if (!print_errors) + /* Don't waste effort tracking the ambig set if + we're not going to print it anyway. */ + ambig_fallback = 1; + else if (!ambig_set) + { + if (__libc_use_alloca (n_options)) + ambig_set = alloca (n_options); + else if ((ambig_set = malloc (n_options)) == NULL) + /* Fall back to simpler error message. */ + ambig_fallback = 1; + else + ambig_malloced = 1; + + if (ambig_set) + { + memset (ambig_set, 0, n_options); + ambig_set[indfound] = 1; + } + } + if (ambig_set) + ambig_set[option_index] = 1; + } + } + } + + if (ambig_set || ambig_fallback) + { + if (print_errors) + { + if (ambig_fallback) + fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"), + argv[0], prefix, d->__nextchar); + else + { + flockfile (stderr); + fprintf (stderr, + _("%s: option '%s%s' is ambiguous; possibilities:"), + argv[0], prefix, d->__nextchar); + + for (option_index = 0; option_index < n_options; option_index++) + if (ambig_set[option_index]) + fprintf (stderr, " '%s%s'", + prefix, longopts[option_index].name); + + /* This must use 'fprintf' even though it's only + printing a single character, so that it goes through + __fxprintf_nocancel when compiled as part of glibc. */ + fprintf (stderr, "\n"); + funlockfile (stderr); + } + } + if (ambig_malloced) + free (ambig_set); + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } + + option_index = indfound; + } + + if (pfound == NULL) + { + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short option, + then it's an error. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + fprintf (stderr, _("%s: unrecognized option '%s%s'\n"), + argv[0], prefix, d->__nextchar); + + d->__nextchar = NULL; + d->optind++; + d->optopt = 0; + return '?'; + } + + /* Otherwise interpret it as a short option. */ + return -1; + } + + /* We have found a matching long option. Consume it. */ + d->optind++; + d->__nextchar = NULL; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option '%s%s' doesn't allow an argument\n"), + argv[0], prefix, pfound->name); + + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option '%s%s' requires an argument\n"), + argv[0], prefix, pfound->name); + + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; +} + +/* Initialize internal data upon the first call to getopt. */ + +static const char * +_getopt_initialize (_GL_UNUSED int argc, + _GL_UNUSED char **argv, const char *optstring, + struct _getopt_data *d, int posixly_correct) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + if (d->optind == 0) + d->optind = 1; + + d->__first_nonopt = d->__last_nonopt = d->optind; + d->__nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + if (optstring[0] == '-') + { + d->__ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + d->__ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct || !!getenv ("POSIXLY_CORRECT")) + d->__ordering = REQUIRE_ORDER; + else + d->__ordering = PERMUTE; + + d->__initialized = 1; + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If 'getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If 'getopt' finds another option character, it returns that character, + updating 'optind' and 'nextchar' so that the next call to 'getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, 'getopt' returns -1. + Then 'optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set 'opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in 'optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in 'optarg', otherwise 'optarg' is set to zero. + + If OPTSTRING starts with '-' or '+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with '--' instead of '-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a '=', or else the in next ARGV-element. + When 'getopt' finds a long-named option, it returns 0 if that option's + 'flag' field is nonzero, the value of the option's 'val' field + if the 'flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of 'struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal_r (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, struct _getopt_data *d, int posixly_correct) +{ + int print_errors = d->opterr; + + if (argc < 1) + return -1; + + d->optarg = NULL; + + if (d->optind == 0 || !d->__initialized) + optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct); + else if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + if (optstring[0] == ':') + print_errors = 0; + + /* Test whether ARGV[optind] points to a non-option argument. */ +#define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') + + if (d->__nextchar == NULL || *d->__nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (d->__last_nonopt > d->optind) + d->__last_nonopt = d->optind; + if (d->__first_nonopt > d->optind) + d->__first_nonopt = d->optind; + + if (d->__ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange (argv, d); + else if (d->__last_nonopt != d->optind) + d->__first_nonopt = d->optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (d->optind < argc && NONOPTION_P) + d->optind++; + d->__last_nonopt = d->optind; + } + + /* The special ARGV-element '--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (d->optind != argc && !strcmp (argv[d->optind], "--")) + { + d->optind++; + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange (argv, d); + else if (d->__first_nonopt == d->__last_nonopt) + d->__first_nonopt = d->optind; + d->__last_nonopt = argc; + + d->optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (d->optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (d->__first_nonopt != d->__last_nonopt) + d->optind = d->__first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (d->__ordering == REQUIRE_ORDER) + return -1; + d->optarg = argv[d->optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Check whether it might be a long option. */ + if (longopts) + { + if (argv[d->optind][1] == '-') + { + /* "--foo" is always a long option. The special option + "--" was handled above. */ + d->__nextchar = argv[d->optind] + 2; + return process_long_option (argc, argv, optstring, longopts, + longind, long_only, d, + print_errors, "--"); + } + + /* If long_only and the ARGV-element has the form "-f", + where f is a valid short option, don't consider it an + abbreviated form of a long option that starts with f. + Otherwise there would be no way to give the -f short + option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an + abbreviation of the long option, just like "--fu", and + not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + if (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))) + { + int code; + d->__nextchar = argv[d->optind] + 1; + code = process_long_option (argc, argv, optstring, longopts, + longind, long_only, d, + print_errors, "-"); + if (code != -1) + return code; + } + } + + /* It is not a long option. Skip the initial punctuation. */ + d->__nextchar = argv[d->optind] + 1; + } + + /* Look at and handle the next short option-character. */ + + { + char c = *d->__nextchar++; + const char *temp = strchr (optstring, c); + + /* Increment 'optind' when we start to process its last character. */ + if (*d->__nextchar == '\0') + ++d->optind; + + if (temp == NULL || c == ':' || c == ';') + { + if (print_errors) + fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); + d->optopt = c; + return '?'; + } + + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL) + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + d->optarg = d->__nextchar; + else if (d->optind == argc) + { + if (print_errors) + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); + + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + d->optarg = argv[d->optind]; + + d->__nextchar = d->optarg; + d->optarg = NULL; + return process_long_option (argc, argv, optstring, longopts, longind, + 0 /* long_only */, d, print_errors, "-W "); + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + d->optind++; + } + else + d->optarg = NULL; + d->__nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); + + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented 'optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + d->__nextchar = NULL; + } + } + return c; + } +} + +int +_getopt_internal (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, int long_only, + int posixly_correct) +{ + int result; + + getopt_data.optind = optind; + getopt_data.opterr = opterr; + + result = _getopt_internal_r (argc, argv, optstring, longopts, + longind, long_only, &getopt_data, + posixly_correct); + + optind = getopt_data.optind; + optarg = getopt_data.optarg; + optopt = getopt_data.optopt; + + return result; +} + +/* glibc gets a LSB-compliant getopt and a POSIX-complaint __posix_getopt. + Standalone applications just get a POSIX-compliant getopt. + POSIX and LSB both require these functions to take 'char *const *argv' + even though this is incorrect (because of the permutation). */ +#define GETOPT_ENTRY(NAME, POSIXLY_CORRECT) \ + int \ + NAME (int argc, char *const *argv, const char *optstring) \ + { \ + return _getopt_internal (argc, (char **)argv, optstring, \ + NULL, NULL, 0, POSIXLY_CORRECT); \ + } + +#ifdef _LIBC +GETOPT_ENTRY(getopt, 0) +GETOPT_ENTRY(__posix_getopt, 1) +#else +GETOPT_ENTRY(getopt, 1) +#endif + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of 'getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value '%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.h new file mode 100644 index 00000000..23377ee5 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt.h @@ -0,0 +1,38 @@ +/* Declarations for getopt. + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Unlike the bulk of the getopt implementation, this file is NOT part + of gnulib; gnulib also has a getopt.h but it is different. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#include + +/* The type of the 'argv' argument to getopt_long and getopt_long_only + is properly 'char **', since both functions may write to the array + (in order to move all the options to the beginning). However, for + compatibility with old versions of LSB, glibc has to use 'char *const *' + instead. */ +#ifndef __getopt_argv_const +# define __getopt_argv_const const +#endif + +#include +#include + +#endif /* getopt.h */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt1.c b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt1.c new file mode 100644 index 00000000..27ace56e --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt1.c @@ -0,0 +1,159 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _LIBC +# include +#endif + +#include "getopt.h" +#include "getopt_int.h" + +int +getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 0, 0); +} + +int +_getopt_long_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 0, d, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *__getopt_argv_const *argv, + const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 1, 0); +} + +int +_getopt_long_only_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 1, d, 0); +} + + +#ifdef TEST + +#include +#include + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static const struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value '%s'\n", optarg); + break; + + case 'd': + printf ("option d with value '%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt_int.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt_int.h new file mode 100644 index 00000000..39eaceac --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/getopt_int.h @@ -0,0 +1,118 @@ +/* Internal declarations for getopt. + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_INT_H +#define _GETOPT_INT_H 1 + +#include + +extern int _getopt_internal (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); + + +/* Reentrant versions which can handle parsing multiple argument + vectors at the same time. */ + +/* Describe how to deal with options that follow non-option ARGV-elements. + + REQUIRE_ORDER means don't recognize them as options; stop option + processing when the first non-option is seen. This is what POSIX + specifies should happen. + + PERMUTE means permute the contents of ARGV as we scan, so that + eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written + to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. + + The special argument '--' forces an end of option-scanning regardless + of the value of 'ordering'. In the case of RETURN_IN_ORDER, only + '--' can cause 'getopt' to return -1 with 'optind' != ARGC. */ + +enum __ord + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + }; + +/* Data type for reentrant functions. */ +struct _getopt_data +{ + /* These have exactly the same meaning as the corresponding global + variables, except that they are used for the reentrant + versions of getopt. */ + int optind; + int opterr; + int optopt; + char *optarg; + + /* Internal members. */ + + /* True if the internal members have been initialized. */ + int __initialized; + + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + char *__nextchar; + + /* See __ord above. */ + enum __ord __ordering; + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. 'first_nonopt' is the index in ARGV of the first + of them; 'last_nonopt' is the index after the last of them. */ + + int __first_nonopt; + int __last_nonopt; +}; + +/* The initializer is necessary to set OPTIND and OPTERR to their + default values and to clear the initialization flag. */ +#define _GETOPT_DATA_INITIALIZER { 1, 1 } + +extern int _getopt_internal_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, struct _getopt_data *__data, + int __posixly_correct); + +extern int _getopt_long_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + struct _getopt_data *__data); + +extern int _getopt_long_only_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, + int *__longind, + struct _getopt_data *__data); + +#endif /* getopt_int.h */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/gettext.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/gettext.h new file mode 100644 index 00000000..10d11590 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/gettext.h @@ -0,0 +1,7 @@ +/* Minimal gettext shim for standalone getopt build on MSVC. */ +#ifndef _GETTEXT_H +#define _GETTEXT_H 1 + +#define gettext(msgid) (msgid) + +#endif /* _GETTEXT_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/unistd.h b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/unistd.h new file mode 100644 index 00000000..26ccc109 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/getopt/unistd.h @@ -0,0 +1,5 @@ +/* Minimal unistd.h shim for standalone getopt build on MSVC. */ +#ifndef _UNISTD_H +#define _UNISTD_H 1 + +#endif /* _UNISTD_H */ diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/hotplugtest.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/hotplugtest.vcxproj new file mode 100644 index 00000000..68c2cdda --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/hotplugtest.vcxproj @@ -0,0 +1,32 @@ + + + + + {99D2AC64-DC66-4422-91CE-6715C403C9E5} + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/init_context.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/init_context.vcxproj new file mode 100644 index 00000000..030956e6 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/init_context.vcxproj @@ -0,0 +1,35 @@ + + + + + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7} + + + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb.sln b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb.sln new file mode 100644 index 00000000..7461c21c --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb.sln @@ -0,0 +1,789 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb_static", "libusb_static.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb_dll", "libusb_dll.vcxproj", "{349EE8FA-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dpfp", "dpfp.vcxproj", "{8C7814A1-FD6E-4185-9EA0-8208119756D4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dpfp_threaded", "dpfp_threaded.vcxproj", "{8C7814A2-FD6E-4185-9EA0-8208119756D4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fxload", "fxload.vcxproj", "{9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt", "getopt.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hotplugtest", "hotplugtest.vcxproj", "{99D2AC64-DC66-4422-91CE-6715C403C9E5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "init_context", "init_context.vcxproj", "{FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "listdevs", "listdevs.vcxproj", "{F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sam3u_benchmark", "sam3u_benchmark.vcxproj", "{861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress", "stress.vcxproj", "{53942EFF-C810-458D-B3CB-EE5CE9F1E781}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress_mt", "stress_mt.vcxproj", "{9EAF1311-2BBB-4177-882B-DA7FA0AD7912}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testlibusb", "testlibusb.vcxproj", "{70828935-325B-4749-B381-0E55EF31AEE8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xusb", "xusb.vcxproj", "{3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "set_option", "set_option.vcxproj", "{35BD5D4B-5102-4A08-81C0-AAF3285FCB01}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Debug-Hotplug|ARM64 = Debug-Hotplug|ARM64 + Debug-Hotplug|Win32 = Debug-Hotplug|Win32 + Debug-Hotplug|x64 = Debug-Hotplug|x64 + Debug-Hotplug-MT|ARM64 = Debug-Hotplug-MT|ARM64 + Debug-Hotplug-MT|Win32 = Debug-Hotplug-MT|Win32 + Debug-Hotplug-MT|x64 = Debug-Hotplug-MT|x64 + Debug-MT|ARM64 = Debug-MT|ARM64 + Debug-MT|Win32 = Debug-MT|Win32 + Debug-MT|x64 = Debug-MT|x64 + Release|ARM64 = Release|ARM64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + Release-Hotplug|ARM64 = Release-Hotplug|ARM64 + Release-Hotplug|Win32 = Release-Hotplug|Win32 + Release-Hotplug|x64 = Release-Hotplug|x64 + Release-Hotplug-MT|ARM64 = Release-Hotplug-MT|ARM64 + Release-Hotplug-MT|Win32 = Release-Hotplug-MT|Win32 + Release-Hotplug-MT|x64 = Release-Hotplug-MT|x64 + Release-MT|ARM64 = Release-MT|ARM64 + Release-MT|Win32 = Release-MT|Win32 + Release-MT|x64 = Release-MT|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|ARM64.Build.0 = Debug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|ARM64.ActiveCfg = Release|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|ARM64.Build.0 = Release|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release-MT|x64.Build.0 = Release-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|ARM64.Build.0 = Debug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|ARM64.ActiveCfg = Release|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|ARM64.Build.0 = Release|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release-MT|x64.Build.0 = Release-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|ARM64.Build.0 = Debug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|Win32.ActiveCfg = Debug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|Win32.Build.0 = Debug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|x64.ActiveCfg = Debug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug|x64.Build.0 = Debug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|ARM64.ActiveCfg = Release|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|ARM64.Build.0 = Release|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|Win32.ActiveCfg = Release|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|Win32.Build.0 = Release|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|x64.ActiveCfg = Release|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release|x64.Build.0 = Release|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {8C7814A1-FD6E-4185-9EA0-8208119756D4}.Release-MT|x64.Build.0 = Release-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|ARM64.Build.0 = Debug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|Win32.ActiveCfg = Debug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|Win32.Build.0 = Debug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|x64.ActiveCfg = Debug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug|x64.Build.0 = Debug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|ARM64.ActiveCfg = Release|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|ARM64.Build.0 = Release|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|Win32.ActiveCfg = Release|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|Win32.Build.0 = Release|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|x64.ActiveCfg = Release|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release|x64.Build.0 = Release|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {8C7814A2-FD6E-4185-9EA0-8208119756D4}.Release-MT|x64.Build.0 = Release-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|ARM64.Build.0 = Debug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|Win32.ActiveCfg = Debug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|Win32.Build.0 = Debug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|x64.ActiveCfg = Debug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug|x64.Build.0 = Debug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|ARM64.ActiveCfg = Release|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|ARM64.Build.0 = Release|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|Win32.ActiveCfg = Release|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|Win32.Build.0 = Release|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|x64.ActiveCfg = Release|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release|x64.Build.0 = Release|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {9E166F7A-A793-9FB6-0A67-F0AED8AE8C88}.Release-MT|x64.Build.0 = Release-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|ARM64.Build.0 = Debug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|ARM64.ActiveCfg = Release|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|ARM64.Build.0 = Release|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release-MT|x64.Build.0 = Release-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|ARM64.Build.0 = Debug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|Win32.ActiveCfg = Debug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|Win32.Build.0 = Debug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|x64.ActiveCfg = Debug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug|x64.Build.0 = Debug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|ARM64.ActiveCfg = Release|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|ARM64.Build.0 = Release|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|Win32.ActiveCfg = Release|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|Win32.Build.0 = Release|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|x64.ActiveCfg = Release|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release|x64.Build.0 = Release|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {99D2AC64-DC66-4422-91CE-6715C403C9E5}.Release-MT|x64.Build.0 = Release-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|ARM64.Build.0 = Debug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|Win32.ActiveCfg = Debug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|Win32.Build.0 = Debug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|x64.ActiveCfg = Debug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug|x64.Build.0 = Debug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|ARM64.ActiveCfg = Release|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|ARM64.Build.0 = Release|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|Win32.ActiveCfg = Release|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|Win32.Build.0 = Release|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|x64.ActiveCfg = Release|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release|x64.Build.0 = Release|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {FB388436-7D25-4A7E-BD3C-DF4EE9FCC8F7}.Release-MT|x64.Build.0 = Release-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|ARM64.Build.0 = Debug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.ActiveCfg = Debug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.Build.0 = Debug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.ActiveCfg = Debug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.Build.0 = Debug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|ARM64.ActiveCfg = Release|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|ARM64.Build.0 = Release|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.ActiveCfg = Release|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.Build.0 = Release|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|x64.ActiveCfg = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|x64.Build.0 = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release-MT|x64.Build.0 = Release-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|ARM64.Build.0 = Debug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|Win32.ActiveCfg = Debug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|Win32.Build.0 = Debug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|x64.ActiveCfg = Debug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug|x64.Build.0 = Debug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|ARM64.ActiveCfg = Release|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|ARM64.Build.0 = Release|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|Win32.ActiveCfg = Release|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|Win32.Build.0 = Release|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|x64.ActiveCfg = Release|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release|x64.Build.0 = Release|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {861CDD5F-59A2-4F34-957E-5C1AF98BE0A4}.Release-MT|x64.Build.0 = Release-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|ARM64.Build.0 = Debug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|Win32.ActiveCfg = Debug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|Win32.Build.0 = Debug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|x64.ActiveCfg = Debug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug|x64.Build.0 = Debug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|ARM64.ActiveCfg = Release|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|ARM64.Build.0 = Release|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|Win32.ActiveCfg = Release|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|Win32.Build.0 = Release|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|x64.ActiveCfg = Release|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release|x64.Build.0 = Release|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {53942EFF-C810-458D-B3CB-EE5CE9F1E781}.Release-MT|x64.Build.0 = Release-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|ARM64.Build.0 = Debug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|Win32.ActiveCfg = Debug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|Win32.Build.0 = Debug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|x64.ActiveCfg = Debug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug|x64.Build.0 = Debug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|ARM64.ActiveCfg = Release|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|ARM64.Build.0 = Release|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|Win32.ActiveCfg = Release|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|Win32.Build.0 = Release|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|x64.ActiveCfg = Release|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release|x64.Build.0 = Release|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912}.Release-MT|x64.Build.0 = Release-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|ARM64.Build.0 = Debug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|Win32.Build.0 = Debug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|x64.ActiveCfg = Debug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug|x64.Build.0 = Debug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|ARM64.ActiveCfg = Release|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|ARM64.Build.0 = Release|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|Win32.ActiveCfg = Release|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|Win32.Build.0 = Release|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|x64.ActiveCfg = Release|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release|x64.Build.0 = Release|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {70828935-325B-4749-B381-0E55EF31AEE8}.Release-MT|x64.Build.0 = Release-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|ARM64.Build.0 = Debug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.ActiveCfg = Debug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.Build.0 = Debug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.ActiveCfg = Debug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.Build.0 = Debug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|ARM64.ActiveCfg = Release|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|ARM64.Build.0 = Release|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.ActiveCfg = Release|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.Build.0 = Release|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|x64.ActiveCfg = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|x64.Build.0 = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release-MT|x64.Build.0 = Release-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|ARM64.Build.0 = Debug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|Win32.ActiveCfg = Debug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|Win32.Build.0 = Debug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|x64.ActiveCfg = Debug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug|x64.Build.0 = Debug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|ARM64.ActiveCfg = Debug-Hotplug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|ARM64.Build.0 = Debug-Hotplug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|Win32.ActiveCfg = Debug-Hotplug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|Win32.Build.0 = Debug-Hotplug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|x64.ActiveCfg = Debug-Hotplug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug|x64.Build.0 = Debug-Hotplug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|ARM64.ActiveCfg = Debug-Hotplug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|ARM64.Build.0 = Debug-Hotplug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|Win32.ActiveCfg = Debug-Hotplug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|Win32.Build.0 = Debug-Hotplug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|x64.ActiveCfg = Debug-Hotplug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-Hotplug-MT|x64.Build.0 = Debug-Hotplug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|ARM64.ActiveCfg = Debug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|ARM64.Build.0 = Debug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|Win32.ActiveCfg = Debug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|Win32.Build.0 = Debug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|x64.ActiveCfg = Debug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Debug-MT|x64.Build.0 = Debug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|ARM64.ActiveCfg = Release|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|ARM64.Build.0 = Release|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|Win32.ActiveCfg = Release|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|Win32.Build.0 = Release|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|x64.ActiveCfg = Release|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release|x64.Build.0 = Release|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|ARM64.ActiveCfg = Release-Hotplug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|ARM64.Build.0 = Release-Hotplug|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|Win32.ActiveCfg = Release-Hotplug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|Win32.Build.0 = Release-Hotplug|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|x64.ActiveCfg = Release-Hotplug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug|x64.Build.0 = Release-Hotplug|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|ARM64.ActiveCfg = Release-Hotplug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|ARM64.Build.0 = Release-Hotplug-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|Win32.ActiveCfg = Release-Hotplug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|Win32.Build.0 = Release-Hotplug-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|x64.ActiveCfg = Release-Hotplug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-Hotplug-MT|x64.Build.0 = Release-Hotplug-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|ARM64.ActiveCfg = Release-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|ARM64.Build.0 = Release-MT|ARM64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|Win32.ActiveCfg = Release-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|Win32.Build.0 = Release-MT|Win32 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|x64.ActiveCfg = Release-MT|x64 + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01}.Release-MT|x64.Build.0 = Release-MT|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AD8203A3-88DD-4212-A273-100DFAD69F6B} + EndGlobalSection +EndGlobal diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_dll.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_dll.vcxproj new file mode 100644 index 00000000..06068ae0 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_dll.vcxproj @@ -0,0 +1,63 @@ + + + + + {349EE8FA-7D25-4909-AAF5-FF3FADE72187} + + + + + + + + + + + + + $(IntDir)..\dll\ + libusb-1.0 + + + + libusb-1.0.rc;%(EmbedManagedResourceFile) + ..\libusb\libusb-1.0.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_static.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_static.vcxproj new file mode 100644 index 00000000..8ebcb1a4 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/libusb_static.vcxproj @@ -0,0 +1,51 @@ + + + + + {349EE8F9-7D25-4909-AAF5-FF3FADE72187} + + + + + + + + + + + + + $(IntDir)..\lib\ + libusb-1.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/listdevs.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/listdevs.vcxproj new file mode 100644 index 00000000..dcaf7927 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/listdevs.vcxproj @@ -0,0 +1,32 @@ + + + + + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87} + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/sam3u_benchmark.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/sam3u_benchmark.vcxproj new file mode 100644 index 00000000..4a1167d7 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/sam3u_benchmark.vcxproj @@ -0,0 +1,33 @@ + + + + + {861cdd5f-59a2-4f34-957e-5c1af98be0a4} + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/set_option.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/set_option.vcxproj new file mode 100644 index 00000000..87cc25cc --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/set_option.vcxproj @@ -0,0 +1,35 @@ + + + + + {35BD5D4B-5102-4A08-81C0-AAF3285FCB01} + + + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress.vcxproj new file mode 100644 index 00000000..ac706d47 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress.vcxproj @@ -0,0 +1,35 @@ + + + + + {53942EFF-C810-458D-B3CB-EE5CE9F1E781} + + + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress_mt.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress_mt.vcxproj new file mode 100644 index 00000000..48b2b68d --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/stress_mt.vcxproj @@ -0,0 +1,33 @@ + + + + + {9EAF1311-2BBB-4177-882B-DA7FA0AD7912} + + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/testlibusb.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/testlibusb.vcxproj new file mode 100644 index 00000000..5f85d047 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/testlibusb.vcxproj @@ -0,0 +1,32 @@ + + + + + {70828935-325B-4749-B381-0E55EF31AEE8} + + + + + + + + + + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/xusb.vcxproj b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/xusb.vcxproj new file mode 100644 index 00000000..df739c3b --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/libusb/msvc/xusb.vcxproj @@ -0,0 +1,38 @@ + + + + + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11} + + + + + + + + + + + + + + + 4127;%(DisableSpecificWarnings) + + + + + + + + + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + false + + + + + + \ No newline at end of file diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/src/constants.rs b/rust_backend/vendor/libusb1-sys-0.7.0/src/constants.rs new file mode 100644 index 00000000..a1d95518 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/src/constants.rs @@ -0,0 +1,160 @@ +use crate::*; +use libc::c_int; + +// libusb_error +pub const LIBUSB_SUCCESS: c_int = 0; +pub const LIBUSB_ERROR_IO: c_int = -1; +pub const LIBUSB_ERROR_INVALID_PARAM: c_int = -2; +pub const LIBUSB_ERROR_ACCESS: c_int = -3; +pub const LIBUSB_ERROR_NO_DEVICE: c_int = -4; +pub const LIBUSB_ERROR_NOT_FOUND: c_int = -5; +pub const LIBUSB_ERROR_BUSY: c_int = -6; +pub const LIBUSB_ERROR_TIMEOUT: c_int = -7; +pub const LIBUSB_ERROR_OVERFLOW: c_int = -8; +pub const LIBUSB_ERROR_PIPE: c_int = -9; +pub const LIBUSB_ERROR_INTERRUPTED: c_int = -10; +pub const LIBUSB_ERROR_NO_MEM: c_int = -11; +pub const LIBUSB_ERROR_NOT_SUPPORTED: c_int = -12; +pub const LIBUSB_ERROR_OTHER: c_int = -99; + +// libusb_transfer_status +pub const LIBUSB_TRANSFER_COMPLETED: c_int = 0; +pub const LIBUSB_TRANSFER_ERROR: c_int = 1; +pub const LIBUSB_TRANSFER_TIMED_OUT: c_int = 2; +pub const LIBUSB_TRANSFER_CANCELLED: c_int = 3; +pub const LIBUSB_TRANSFER_STALL: c_int = 4; +pub const LIBUSB_TRANSFER_NO_DEVICE: c_int = 5; +pub const LIBUSB_TRANSFER_OVERFLOW: c_int = 6; + +pub const LIBUSB_TRANSFER_SHORT_NOT_OK: u8 = 1 << 0; +pub const LIBUSB_TRANSFER_FREE_BUFFER: u8 = 1 << 1; +pub const LIBUSB_TRANSFER_FREE_TRANSFER: u8 = 1 << 2; +pub const LIBUSB_TRANSFER_ADD_ZERO_PACKET: u8 = 1 << 3; + +// libusb_capability +pub const LIBUSB_CAP_HAS_CAPABILITY: u32 = 0x0000; +pub const LIBUSB_CAP_HAS_HOTPLUG: u32 = 0x0001; +pub const LIBUSB_CAP_HAS_HID_ACCESS: u32 = 0x0100; +pub const LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: u32 = 0x0101; + +//// libusb_log_level +pub const LIBUSB_LOG_LEVEL_NONE: c_int = 0; +pub const LIBUSB_LOG_LEVEL_ERROR: c_int = 1; +pub const LIBUSB_LOG_LEVEL_WARNING: c_int = 2; +pub const LIBUSB_LOG_LEVEL_INFO: c_int = 3; +pub const LIBUSB_LOG_LEVEL_DEBUG: c_int = 4; + +// libusb_class_code +pub const LIBUSB_CLASS_PER_INTERFACE: u8 = 0; +pub const LIBUSB_CLASS_AUDIO: u8 = 1; +pub const LIBUSB_CLASS_COMM: u8 = 2; +pub const LIBUSB_CLASS_HID: u8 = 3; +pub const LIBUSB_CLASS_PHYSICAL: u8 = 5; +pub const LIBUSB_CLASS_PRINTER: u8 = 7; +pub const LIBUSB_CLASS_IMAGE: u8 = 6; +pub const LIBUSB_CLASS_MASS_STORAGE: u8 = 8; +pub const LIBUSB_CLASS_HUB: u8 = 9; +pub const LIBUSB_CLASS_DATA: u8 = 10; +pub const LIBUSB_CLASS_SMART_CARD: u8 = 0x0B; +pub const LIBUSB_CLASS_CONTENT_SECURITY: u8 = 0x0D; +pub const LIBUSB_CLASS_VIDEO: u8 = 0x0E; +pub const LIBUSB_CLASS_PERSONAL_HEALTHCARE: u8 = 0x0F; +pub const LIBUSB_CLASS_DIAGNOSTIC_DEVICE: u8 = 0xDC; +pub const LIBUSB_CLASS_WIRELESS: u8 = 0xE0; +pub const LIBUSB_CLASS_APPLICATION: u8 = 0xFE; +pub const LIBUSB_CLASS_VENDOR_SPEC: u8 = 0xFF; + +// libusb_speed +pub const LIBUSB_SPEED_UNKNOWN: c_int = 0; +pub const LIBUSB_SPEED_LOW: c_int = 1; +pub const LIBUSB_SPEED_FULL: c_int = 2; +pub const LIBUSB_SPEED_HIGH: c_int = 3; +pub const LIBUSB_SPEED_SUPER: c_int = 4; +pub const LIBUSB_SPEED_SUPER_PLUS: c_int = 5; + +// libusb_descriptor_type +pub const LIBUSB_DT_DEVICE: u8 = 0x01; +pub const LIBUSB_DT_CONFIG: u8 = 0x02; +pub const LIBUSB_DT_STRING: u8 = 0x03; +pub const LIBUSB_DT_INTERFACE: u8 = 0x04; +pub const LIBUSB_DT_ENDPOINT: u8 = 0x05; +pub const LIBUSB_DT_BOS: u8 = 0x0F; +pub const LIBUSB_DT_DEVICE_CAPABILITY: u8 = 0x10; +pub const LIBUSB_DT_HID: u8 = 0x21; +pub const LIBUSB_DT_REPORT: u8 = 0x22; +pub const LIBUSB_DT_PHYSICAL: u8 = 0x23; +pub const LIBUSB_DT_HUB: u8 = 0x29; +pub const LIBUSB_DT_SUPERSPEED_HUB: u8 = 0x2A; +pub const LIBUSB_DT_SS_ENDPOINT_COMPANION: u8 = 0x30; + +// libusb_endpoint_direction +pub const LIBUSB_ENDPOINT_ADDRESS_MASK: u8 = 0x0F; +pub const LIBUSB_ENDPOINT_DIR_MASK: u8 = 0x80; +pub const LIBUSB_ENDPOINT_IN: u8 = 0x80; +pub const LIBUSB_ENDPOINT_OUT: u8 = 0x00; + +// libusb_transfer_type +pub const LIBUSB_TRANSFER_TYPE_MASK: u8 = 0x03; +pub const LIBUSB_TRANSFER_TYPE_CONTROL: u8 = 0; +pub const LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: u8 = 1; +pub const LIBUSB_TRANSFER_TYPE_BULK: u8 = 2; +pub const LIBUSB_TRANSFER_TYPE_INTERRUPT: u8 = 3; +pub const LIBUSB_TRANSFER_TYPE_BULK_STREAM: u8 = 4; + +// libusb_iso_sync_type +pub const LIBUSB_ISO_SYNC_TYPE_MASK: u8 = 0x0C; +pub const LIBUSB_ISO_SYNC_TYPE_NONE: u8 = 0; +pub const LIBUSB_ISO_SYNC_TYPE_ASYNC: u8 = 1; +pub const LIBUSB_ISO_SYNC_TYPE_ADAPTIVE: u8 = 2; +pub const LIBUSB_ISO_SYNC_TYPE_SYNC: u8 = 3; + +// libusb_iso_usage_type +pub const LIBUSB_ISO_USAGE_TYPE_MASK: u8 = 0x30; +pub const LIBUSB_ISO_USAGE_TYPE_DATA: u8 = 0; +pub const LIBUSB_ISO_USAGE_TYPE_FEEDBACK: u8 = 1; +pub const LIBUSB_ISO_USAGE_TYPE_IMPLICIT: u8 = 2; + +// libusb_request_type +pub const LIBUSB_REQUEST_TYPE_STANDARD: u8 = 0x00 << 5; +pub const LIBUSB_REQUEST_TYPE_CLASS: u8 = 0x01 << 5; +pub const LIBUSB_REQUEST_TYPE_VENDOR: u8 = 0x02 << 5; +pub const LIBUSB_REQUEST_TYPE_RESERVED: u8 = 0x03 << 5; + +// libusb_request_recipient +pub const LIBUSB_RECIPIENT_DEVICE: u8 = 0x00; +pub const LIBUSB_RECIPIENT_INTERFACE: u8 = 0x01; +pub const LIBUSB_RECIPIENT_ENDPOINT: u8 = 0x02; +pub const LIBUSB_RECIPIENT_OTHER: u8 = 0x03; + +// libusb_standard_request +pub const LIBUSB_REQUEST_GET_STATUS: u8 = 0x00; +pub const LIBUSB_REQUEST_CLEAR_FEATURE: u8 = 0x01; +pub const LIBUSB_REQUEST_SET_FEATURE: u8 = 0x03; +pub const LIBUSB_REQUEST_SET_ADDRESS: u8 = 0x05; +pub const LIBUSB_REQUEST_GET_DESCRIPTOR: u8 = 0x06; +pub const LIBUSB_REQUEST_SET_DESCRIPTOR: u8 = 0x07; +pub const LIBUSB_REQUEST_GET_CONFIGURATION: u8 = 0x08; +pub const LIBUSB_REQUEST_SET_CONFIGURATION: u8 = 0x09; +pub const LIBUSB_REQUEST_GET_INTERFACE: u8 = 0x0A; +pub const LIBUSB_REQUEST_SET_INTERFACE: u8 = 0x0B; +pub const LIBUSB_REQUEST_SYNCH_FRAME: u8 = 0x0C; +pub const LIBUSB_REQUEST_SET_SEL: u8 = 0x30; +pub const LIBUSB_SET_ISOCH_DELAY: u8 = 0x31; + +// libusb_hotplug +pub const LIBUSB_HOTPLUG_NO_FLAGS: c_int = 0; +pub const LIBUSB_HOTPLUG_ENUMERATE: c_int = 1 << 0; +pub const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: c_int = 0x01; +pub const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: c_int = 0x02; +pub const LIBUSB_HOTPLUG_MATCH_ANY: c_int = -1; + +pub const LIBUSB_OPTION_LOG_LEVEL: u32 = 0x00; +pub const LIBUSB_OPTION_USE_USBDK: u32 = 0x01; +pub const LIBUSB_OPTION_WEAK_AUTHORITY: u32 = 0x02; +pub const LIBUSB_OPTION_NO_DEVICE_DISCOVERY: u32 = 0x02; + +// libusb_log_cb_mode +pub const LIBUSB_LOG_CB_GLOBAL: libusb_log_cb_mode = 1 << 0; +pub const LIBUSB_LOG_CB_CONTEXT: libusb_log_cb_mode = 1 << 1; + +pub const LIBUSB_CONTROL_SETUP_SIZE: usize = std::mem::size_of::(); diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/src/lib.rs b/rust_backend/vendor/libusb1-sys-0.7.0/src/lib.rs new file mode 100644 index 00000000..b0ac87db --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/src/lib.rs @@ -0,0 +1,696 @@ +#![allow(non_camel_case_types)] + +pub mod constants; + +use self::constants::*; +use libc::{c_char, c_int, c_short, c_uchar, c_uint, c_void, ssize_t, timeval}; + +#[repr(C)] +pub struct libusb_context { + __private: c_void, +} + +#[repr(C)] +pub struct libusb_device { + __private: c_void, +} + +#[repr(C)] +pub struct libusb_device_handle { + __private: c_void, +} + +#[repr(C)] +pub struct libusb_version { + pub major: u16, + pub minor: u16, + pub micro: u16, + pub nano: u16, + pub rc: *const c_char, + pub describe: *const c_char, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_device_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bcdUSB: u16, + pub bDeviceClass: u8, + pub bDeviceSubClass: u8, + pub bDeviceProtocol: u8, + pub bMaxPacketSize0: u8, + pub idVendor: u16, + pub idProduct: u16, + pub bcdDevice: u16, + pub iManufacturer: u8, + pub iProduct: u8, + pub iSerialNumber: u8, + pub bNumConfigurations: u8, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_config_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub wTotalLength: u16, + pub bNumInterfaces: u8, + pub bConfigurationValue: u8, + pub iConfiguration: u8, + pub bmAttributes: u8, + pub bMaxPower: u8, + pub interface: *const libusb_interface, + pub extra: *const c_uchar, + pub extra_length: c_int, +} + +#[repr(C)] +pub struct libusb_interface { + pub altsetting: *const libusb_interface_descriptor, + pub num_altsetting: c_int, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_interface_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bInterfaceNumber: u8, + pub bAlternateSetting: u8, + pub bNumEndpoints: u8, + pub bInterfaceClass: u8, + pub bInterfaceSubClass: u8, + pub bInterfaceProtocol: u8, + pub iInterface: u8, + pub endpoint: *const libusb_endpoint_descriptor, + pub extra: *const c_uchar, + pub extra_length: c_int, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_endpoint_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bEndpointAddress: u8, + pub bmAttributes: u8, + pub wMaxPacketSize: u16, + pub bInterval: u8, + pub bRefresh: u8, + pub bSynchAddress: u8, + pub extra: *const c_uchar, + pub extra_length: c_int, +} + +#[repr(C)] +pub struct libusb_iso_packet_descriptor { + pub length: c_uint, + pub actual_length: c_uint, + pub status: c_int, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_ss_endpoint_companion_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bMaxBurst: u8, + pub bmAttributes: u8, + pub wBytesPerInterval: u16, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_bos_dev_capability_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDevCapabilityType: u8, + pub dev_capability_data: [u8; 0], +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_bos_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub wTotalLength: u16, + pub bNumDeviceCaps: u8, + pub dev_capability: [libusb_bos_dev_capability_descriptor; 0], +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_usb_2_0_extension_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDevCapabilityType: u8, + pub bmAttributes: u32, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_ss_usb_device_capability_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDevCapabilityType: u8, + pub bmAttributes: u8, + pub wSpeedSupported: u16, + pub bFunctionalitySupport: u8, + pub bU1DevExitLat: u8, + pub bU2DevExitLat: u8, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct libusb_container_id_descriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDevCapabilityType: u8, + pub bReserved: u8, + pub ContainerId: [u8; 16], +} + +#[allow(non_snake_case)] +#[repr(C, packed)] +pub struct libusb_control_setup { + pub bmRequestType: u8, + pub bRequest: u8, + pub wValue: u16, + pub wIndex: u16, + pub wLength: u16, +} + +#[repr(C)] +pub struct libusb_transfer { + pub dev_handle: *mut libusb_device_handle, + pub flags: u8, + pub endpoint: c_uchar, + pub transfer_type: c_uchar, + pub timeout: c_uint, + pub status: c_int, + pub length: c_int, + pub actual_length: c_int, + pub callback: libusb_transfer_cb_fn, + pub user_data: *mut c_void, + pub buffer: *mut c_uchar, + pub num_iso_packets: c_int, + pub iso_packet_desc: [libusb_iso_packet_descriptor; 0], +} + +#[repr(C)] +pub struct libusb_pollfd { + pub fd: c_int, + pub events: c_short, +} + +pub type libusb_hotplug_callback_handle = c_int; +pub type libusb_hotplug_flag = c_int; +pub type libusb_hotplug_event = c_int; + +pub type libusb_log_cb_mode = c_int; + +pub type libusb_transfer_cb_fn = extern "system" fn(*mut libusb_transfer); +pub type libusb_pollfd_added_cb = extern "system" fn(c_int, c_short, *mut c_void); +pub type libusb_pollfd_removed_cb = extern "system" fn(c_int, *mut c_void); +pub type libusb_hotplug_callback_fn = extern "system" fn( + ctx: *mut libusb_context, + device: *mut libusb_device, + event: libusb_hotplug_event, + user_data: *mut c_void, +) -> c_int; + +pub type libusb_log_cb = extern "system" fn(context: *mut libusb_context, c_int, *mut c_void); + +extern "system" { + pub fn libusb_get_version() -> *const libusb_version; + pub fn libusb_has_capability(capability: u32) -> c_int; + pub fn libusb_error_name(errcode: c_int) -> *const c_char; + pub fn libusb_setlocale(locale: *const c_char) -> c_int; + pub fn libusb_strerror(errcode: c_int) -> *const c_char; + + pub fn libusb_init(context: *mut *mut libusb_context) -> c_int; + pub fn libusb_exit(context: *mut libusb_context); + pub fn libusb_set_debug(context: *mut libusb_context, level: c_int); + pub fn libusb_set_log_cb(context: *mut libusb_context, cb: Option, mode: c_int); + + pub fn libusb_get_device_list( + context: *mut libusb_context, + list: *mut *const *mut libusb_device, + ) -> ssize_t; + pub fn libusb_free_device_list(list: *const *mut libusb_device, unref_devices: c_int); + pub fn libusb_get_parent(dev: *mut libusb_device) -> *mut libusb_device; + pub fn libusb_get_device(dev_handle: *mut libusb_device_handle) -> *mut libusb_device; + + pub fn libusb_ref_device(dev: *mut libusb_device) -> *mut libusb_device; + pub fn libusb_unref_device(dev: *mut libusb_device); + + pub fn libusb_get_device_descriptor( + dev: *const libusb_device, + desc: *mut libusb_device_descriptor, + ) -> c_int; + pub fn libusb_get_config_descriptor( + dev: *const libusb_device, + index: u8, + config: *mut *const libusb_config_descriptor, + ) -> c_int; + pub fn libusb_get_active_config_descriptor( + dev: *const libusb_device, + config: *mut *const libusb_config_descriptor, + ) -> c_int; + pub fn libusb_get_config_descriptor_by_value( + dev: *const libusb_device, + bConfigurationValue: u8, + config: *mut *const libusb_config_descriptor, + ) -> c_int; + pub fn libusb_free_config_descriptor(config: *const libusb_config_descriptor); + + pub fn libusb_get_bus_number(dev: *const libusb_device) -> u8; + pub fn libusb_get_port_number(dev: *mut libusb_device) -> u8; + pub fn libusb_get_port_numbers( + dev: *mut libusb_device, + port_numbers: *mut u8, + port_numbers_len: c_int, + ) -> c_int; + pub fn libusb_get_device_address(dev: *const libusb_device) -> u8; + pub fn libusb_get_device_speed(dev: *const libusb_device) -> c_int; + pub fn libusb_get_max_packet_size(dev: *const libusb_device, endpoint: c_uchar) -> c_int; + pub fn libusb_get_max_iso_packet_size(dev: *const libusb_device, endpoint: c_uchar) -> c_int; + + pub fn libusb_wrap_sys_device( + context: *mut libusb_context, + sys_dev: *mut c_int, + handle: *mut *mut libusb_device_handle, + ) -> c_int; + pub fn libusb_open(dev: *const libusb_device, handle: *mut *mut libusb_device_handle) -> c_int; + pub fn libusb_close(dev_handle: *mut libusb_device_handle); + pub fn libusb_open_device_with_vid_pid( + context: *mut libusb_context, + vendor_id: u16, + product_id: u16, + ) -> *mut libusb_device_handle; + pub fn libusb_reset_device(dev_handle: *mut libusb_device_handle) -> c_int; + pub fn libusb_clear_halt(dev_handle: *mut libusb_device_handle, endpoint: c_uchar) -> c_int; + pub fn libusb_alloc_streams( + dev_handle: *mut libusb_device_handle, + num_streams: u32, + endpoints: *mut c_uchar, + num_endpoints: c_int, + ) -> c_int; + pub fn libusb_free_streams( + dev_handle: *mut libusb_device_handle, + endpoints: *mut c_uchar, + num_endpoints: c_int, + ) -> c_int; + pub fn libusb_get_string_descriptor_ascii( + dev_handle: *mut libusb_device_handle, + desc_index: u8, + data: *mut c_uchar, + length: c_int, + ) -> c_int; + + pub fn libusb_get_configuration( + dev_handle: *mut libusb_device_handle, + config: *mut c_int, + ) -> c_int; + pub fn libusb_set_configuration(dev_handle: *mut libusb_device_handle, config: c_int) -> c_int; + + pub fn libusb_get_ss_endpoint_companion_descriptor( + context: *mut libusb_context, + endpoint: *const libusb_endpoint_descriptor, + ep_comp: *mut *const libusb_ss_endpoint_companion_descriptor, + ) -> c_int; + pub fn libusb_free_ss_endpoint_companion_descriptor( + ep_comp: *mut libusb_ss_endpoint_companion_descriptor, + ); + pub fn libusb_get_bos_descriptor( + dev_handle: *mut libusb_device_handle, + bos: *mut *const libusb_bos_descriptor, + ) -> c_int; + pub fn libusb_free_bos_descriptor(bos: *mut libusb_bos_descriptor); + pub fn libusb_get_usb_2_0_extension_descriptor( + context: *mut libusb_context, + dev_cap: *mut libusb_bos_dev_capability_descriptor, + usb_2_0_extension: *mut *const libusb_usb_2_0_extension_descriptor, + ) -> c_int; + pub fn libusb_free_usb_2_0_extension_descriptor( + usb_2_0_extension: *mut libusb_usb_2_0_extension_descriptor, + ); + pub fn libusb_get_ss_usb_device_capability_descriptor( + context: *mut libusb_context, + dev_cap: *mut libusb_bos_dev_capability_descriptor, + ss_usb_device_cap: *mut *const libusb_ss_usb_device_capability_descriptor, + ) -> c_int; + pub fn libusb_free_ss_usb_device_capability_descriptor( + ss_usb_device_cap: *mut libusb_ss_usb_device_capability_descriptor, + ); + pub fn libusb_get_container_id_descriptor( + context: *mut libusb_context, + dev_cap: *mut libusb_bos_dev_capability_descriptor, + container_id: *mut *const libusb_container_id_descriptor, + ) -> c_int; + pub fn libusb_free_container_id_descriptor(container_id: *mut libusb_container_id_descriptor); + + pub fn libusb_set_auto_detach_kernel_driver( + dev_handle: *mut libusb_device_handle, + enable: c_int, + ) -> c_int; + pub fn libusb_kernel_driver_active( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + ) -> c_int; + pub fn libusb_detach_kernel_driver( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + ) -> c_int; + pub fn libusb_attach_kernel_driver( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + ) -> c_int; + + pub fn libusb_claim_interface( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + ) -> c_int; + pub fn libusb_release_interface( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + ) -> c_int; + pub fn libusb_set_interface_alt_setting( + dev_handle: *mut libusb_device_handle, + interface_number: c_int, + alternate_setting: c_int, + ) -> c_int; + + pub fn libusb_interrupt_transfer( + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + data: *mut c_uchar, + length: c_int, + transferred: *mut c_int, + timeout: c_uint, + ) -> c_int; + pub fn libusb_bulk_transfer( + dev_handle: *mut libusb_device_handle, + endpoint: c_uchar, + data: *mut c_uchar, + length: c_int, + transferred: *mut c_int, + timeout: c_uint, + ) -> c_int; + pub fn libusb_control_transfer( + dev_handle: *mut libusb_device_handle, + request_type: u8, + request: u8, + value: u16, + index: u16, + data: *mut c_uchar, + length: u16, + timeout: c_uint, + ) -> c_int; + + pub fn libusb_alloc_transfer(iso_packets: c_int) -> *mut libusb_transfer; + pub fn libusb_submit_transfer(transfer: *mut libusb_transfer) -> c_int; + pub fn libusb_cancel_transfer(transfer: *mut libusb_transfer) -> c_int; + pub fn libusb_free_transfer(transfer: *mut libusb_transfer); + pub fn libusb_transfer_set_stream_id(transfer: *mut libusb_transfer, stream_id: u32); + pub fn libusb_transfer_get_stream_id(transfer: *mut libusb_transfer) -> u32; + + pub fn libusb_handle_events(context: *mut libusb_context) -> c_int; + pub fn libusb_handle_events_timeout(context: *mut libusb_context, tv: *const timeval) -> c_int; + pub fn libusb_handle_events_completed( + context: *mut libusb_context, + completed: *mut c_int, + ) -> c_int; + pub fn libusb_handle_events_timeout_completed( + context: *mut libusb_context, + tv: *const timeval, + completed: *mut c_int, + ) -> c_int; + pub fn libusb_handle_events_locked(context: *mut libusb_context, tv: *const timeval) -> c_int; + pub fn libusb_interrupt_event_handler(context: *mut libusb_context); + + pub fn libusb_try_lock_events(context: *mut libusb_context) -> c_int; + pub fn libusb_lock_events(context: *mut libusb_context); + pub fn libusb_unlock_events(context: *mut libusb_context); + pub fn libusb_event_handling_ok(context: *mut libusb_context) -> c_int; + pub fn libusb_event_handler_active(context: *mut libusb_context) -> c_int; + pub fn libusb_lock_event_waiters(context: *mut libusb_context); + pub fn libusb_unlock_event_waiters(context: *mut libusb_context); + pub fn libusb_wait_for_event(context: *mut libusb_context, tv: *const timeval) -> c_int; + + pub fn libusb_pollfds_handle_timeouts(context: *mut libusb_context) -> c_int; + pub fn libusb_get_next_timeout(context: *mut libusb_context, tv: *mut timeval) -> c_int; + pub fn libusb_get_pollfds(context: *mut libusb_context) -> *const *mut libusb_pollfd; + pub fn libusb_set_pollfd_notifiers( + context: *mut libusb_context, + added_cb: Option, + removed_cb: Option, + user_data: *mut c_void, + ); + pub fn libusb_free_pollfds(pollfds: *const *mut libusb_pollfd); + pub fn libusb_hotplug_register_callback( + ctx: *mut libusb_context, + events: c_int, + flags: c_int, + vendor_id: c_int, + product_id: c_int, + dev_class: c_int, + cb_fn: libusb_hotplug_callback_fn, + user_data: *mut c_void, + callback_handle: *mut libusb_hotplug_callback_handle, + ) -> c_int; + pub fn libusb_hotplug_deregister_callback( + ctx: *mut libusb_context, + callback_handle: libusb_hotplug_callback_handle, + ); + + pub fn libusb_hotplug_get_user_data( + ctx: *mut libusb_context, + callback_handle: libusb_hotplug_callback_handle, + ) -> *mut c_void; +} + +// As libusb_set_option is a variatic function, it must use "C" +// calling conventions +extern "C" { + pub fn libusb_set_option(ctx: *mut libusb_context, option: u32, ...) -> c_int; +} + +// defined as static inline in libusb.h +#[inline] +pub unsafe fn libusb_get_string_descriptor( + dev_handle: *mut libusb_device_handle, + desc_index: u8, + langid: u16, + data: *mut c_uchar, + length: c_int, +) -> c_int { + libusb_control_transfer( + dev_handle, + LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, + u16::from(LIBUSB_DT_STRING) << 8 | u16::from(desc_index), + langid, + data, + length as u16, + 1000, + ) +} + +// defined as static inline in libusb.h +#[inline] +pub unsafe fn libusb_get_descriptor( + dev_handle: *mut libusb_device_handle, + desc_type: u8, + desc_index: u8, + langid: u16, + data: *mut c_uchar, + length: c_int, +) -> c_int { + libusb_control_transfer( + dev_handle, + LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, + u16::from(desc_type) << 8 | u16::from(desc_index), + langid, + data, + length as u16, + 1000, + ) +} + +#[inline] +pub unsafe fn libusb_control_transfer_get_data(transfer: *mut libusb_transfer) -> *mut c_uchar { + (*transfer).buffer.add(constants::LIBUSB_CONTROL_SETUP_SIZE) +} + +#[inline] +pub unsafe fn libusb_control_transfer_get_setup( + transfer: *mut libusb_transfer, +) -> *mut libusb_control_setup { + (*transfer).buffer as *mut _ +} + +#[allow(non_snake_case)] +#[inline] +pub unsafe fn libusb_fill_control_setup( + buffer: *mut c_uchar, + bmRequestType: u8, + bRequest: u8, + wValue: u16, + wIndex: u16, + wLength: u16, +) { + let setup: *mut libusb_control_setup = buffer as *mut _; + (*setup).bmRequestType = bmRequestType; + (*setup).bRequest = bRequest; + (*setup).wValue = wValue.to_le(); + (*setup).wIndex = wIndex.to_le(); + (*setup).wLength = wLength.to_le(); +} + +#[inline] +pub unsafe fn libusb_fill_control_transfer( + transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + buffer: *mut u8, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint, +) { + let setup: *mut libusb_control_setup = buffer as *mut c_void as *mut libusb_control_setup; + + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = 0; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_CONTROL; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + if !buffer.is_null() { + (*transfer).length = + (constants::LIBUSB_CONTROL_SETUP_SIZE as u16 + u16::from_le((*setup).wLength)).into(); + } + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +#[inline] +pub unsafe fn libusb_fill_bulk_transfer( + transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: u8, + buffer: *mut u8, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint, +) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_BULK; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +#[inline] +pub unsafe fn libusb_fill_bulk_stream_transfer( + transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: u8, + stream_id: u32, + buffer: *mut u8, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint, +) { + libusb_fill_bulk_transfer( + transfer, dev_handle, endpoint, buffer, length, callback, user_data, timeout, + ); + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +#[inline] +pub unsafe fn libusb_fill_interrupt_transfer( + transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: u8, + buffer: *mut u8, + length: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint, +) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +#[inline] +pub unsafe fn libusb_fill_iso_transfer( + transfer: *mut libusb_transfer, + dev_handle: *mut libusb_device_handle, + endpoint: u8, + buffer: *mut u8, + length: c_int, + num_iso_packets: c_int, + callback: libusb_transfer_cb_fn, + user_data: *mut c_void, + timeout: c_uint, +) { + (*transfer).dev_handle = dev_handle; + (*transfer).endpoint = endpoint; + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + (*transfer).timeout = timeout; + (*transfer).buffer = buffer; + (*transfer).length = length; + (*transfer).num_iso_packets = num_iso_packets; + (*transfer).user_data = user_data; + (*transfer).callback = callback; +} + +#[inline] +pub unsafe fn libusb_set_iso_packet_lengths(transfer: *mut libusb_transfer, length: c_uint) { + for i in 0..(*transfer).num_iso_packets { + (*(*transfer).iso_packet_desc.as_mut_ptr().add(i as usize)).length = length; + } +} + +#[inline] +pub unsafe fn libusb_get_iso_packet_buffer( + transfer: *mut libusb_transfer, + packet: c_uint, +) -> *mut c_uchar { + if packet as c_int >= (*transfer).num_iso_packets { + return std::ptr::null_mut(); + } + let mut offset = 0; + for i in 0..packet { + offset += (*(*transfer).iso_packet_desc.as_mut_ptr().add(i as usize)).length; + } + + (*transfer).buffer.add(offset as usize) +} + +#[inline] +pub unsafe fn libusb_get_iso_packet_buffer_simple( + transfer: *mut libusb_transfer, + packet: c_uint, +) -> *mut c_uchar { + if packet as c_int >= (*transfer).num_iso_packets { + return std::ptr::null_mut(); + } + + (*transfer) + .buffer + .add(((*(*transfer).iso_packet_desc.as_mut_ptr().add(0)).length * packet) as usize) +} diff --git a/rust_backend/vendor/libusb1-sys-0.7.0/tests/test.rs b/rust_backend/vendor/libusb1-sys-0.7.0/tests/test.rs new file mode 100644 index 00000000..42cd5a01 --- /dev/null +++ b/rust_backend/vendor/libusb1-sys-0.7.0/tests/test.rs @@ -0,0 +1,160 @@ +use libusb1_sys as ffi; + +#[test] +fn test_version() { + use std::{ffi::CStr, str}; + let version = unsafe { &*ffi::libusb_get_version() }; + let rc = str::from_utf8(unsafe { CStr::from_ptr(version.rc) }.to_bytes()).unwrap_or(""); + let describe = + str::from_utf8(unsafe { CStr::from_ptr(version.describe) }.to_bytes()).unwrap_or(""); + + assert_eq!(version.major, 1); + assert_eq!(version.minor, 0); + println!( + "libusb v{}.{}.{}.{}{} {}", + version.major, version.minor, version.micro, version.nano, rc, describe + ); +} + +#[test] +fn test_init_and_exit() { + let mut context: *mut ffi::libusb_context = std::ptr::null_mut(); + for i in 0..=100 { + match unsafe { ffi::libusb_init(&mut context) } { + 0 => (), + err => panic!("Failed to init libusb on iteration {}: {}", i, err), + } + unsafe { + ffi::libusb_exit(context); + } + #[cfg(target_os = "macos")] + std::thread::sleep(std::time::Duration::from_millis(1)); + context = std::ptr::null_mut(); + } +} + +#[test] +fn test_get_device_list() { + let mut context = std::mem::MaybeUninit::<*mut ffi::libusb_context>::uninit(); + match unsafe { ffi::libusb_init(context.as_mut_ptr()) } { + 0 => (), + err => panic!("Failed to init libusb {}", err), + } + let mut list = std::mem::MaybeUninit::<*const *mut ffi::libusb_device>::uninit(); + let list_size = + unsafe { ffi::libusb_get_device_list(context.assume_init(), list.as_mut_ptr()) }; + if list_size < 0 || unsafe { list.assume_init().is_null() } { + panic!("Failed to get device list {} {:p}", -list_size, unsafe { + list.assume_init() + }); + } + unsafe { + ffi::libusb_free_device_list(list.assume_init(), 1); + } + unsafe { + ffi::libusb_exit(context.assume_init()); + } +} + +#[test] +fn test_fill_control_setup() { + let mut buf = [0u8; ffi::constants::LIBUSB_CONTROL_SETUP_SIZE + 1]; + unsafe { + ffi::libusb_fill_control_setup( + buf.as_mut_ptr(), + ffi::constants::LIBUSB_REQUEST_TYPE_VENDOR | ffi::constants::LIBUSB_ENDPOINT_OUT, + 0x04, + 0x4e, + 0, + (buf.len() - ffi::constants::LIBUSB_CONTROL_SETUP_SIZE) as u16, + ); + } + buf[ffi::constants::LIBUSB_CONTROL_SETUP_SIZE] = 0x01; + let setup: *mut ffi::libusb_control_setup = buf.as_mut_ptr() as *mut _; + + assert_eq!( + unsafe { (*setup).bmRequestType }, + ffi::constants::LIBUSB_REQUEST_TYPE_VENDOR | ffi::constants::LIBUSB_ENDPOINT_OUT + ); + assert_eq!(unsafe { (*setup).bRequest }, 0x04); + assert_eq!(unsafe { u16::from_le((*setup).wValue) }, 0x4e); + assert_eq!(unsafe { u16::from_le((*setup).wIndex) }, 0); + assert_eq!(unsafe { u16::from_le((*setup).wLength) }, 1); +} + +#[test] +fn test_fill_control_transfer() { + extern "system" fn callback(_transfer: *mut ffi::libusb_transfer) {} + + let mut buf = [0u8; ffi::constants::LIBUSB_CONTROL_SETUP_SIZE + 1]; + unsafe { + ffi::libusb_fill_control_setup( + buf.as_mut_ptr(), + ffi::constants::LIBUSB_REQUEST_TYPE_VENDOR | ffi::constants::LIBUSB_ENDPOINT_OUT, + 0x04, + 0x4e, + 0, + (buf.len() - ffi::constants::LIBUSB_CONTROL_SETUP_SIZE) as u16, + ); + } + buf[ffi::constants::LIBUSB_CONTROL_SETUP_SIZE] = 0x05; + + let mut transfer = std::mem::MaybeUninit::::uninit(); + + unsafe { + ffi::libusb_fill_control_transfer( + transfer.as_mut_ptr(), + std::ptr::null_mut(), + buf.as_mut_ptr(), + callback, + std::ptr::null_mut(), + 1000, + ); + } + let transfer = unsafe { &mut transfer.assume_init() }; + assert_eq!(transfer.endpoint, 0); + assert_eq!( + transfer.length as usize, + ffi::constants::LIBUSB_CONTROL_SETUP_SIZE + 1 + ); + assert_eq!(transfer.timeout, 1000); + assert_eq!( + transfer.transfer_type, + ffi::constants::LIBUSB_TRANSFER_TYPE_CONTROL + ); + assert_eq!(transfer.buffer, buf.as_mut_ptr()); + + let data = unsafe { + std::slice::from_raw_parts(ffi::libusb_control_transfer_get_data(transfer as *mut _), 1) + }; + assert_eq!(data[0], 0x05); +} + +#[test] +fn test_fill_bulk_transfer() { + extern "system" fn callback(_transfer: *mut ffi::libusb_transfer) {} + + let mut transfer = std::mem::MaybeUninit::::uninit(); + let mut buf = [5u8; 64]; + unsafe { + ffi::libusb_fill_bulk_transfer( + transfer.as_mut_ptr(), + std::ptr::null_mut(), + 0x80, + buf.as_mut_ptr(), + buf.len() as libc::c_int, + callback, + std::ptr::null_mut(), + 1000, + ); + } + let transfer = unsafe { &transfer.assume_init() }; + assert_eq!(transfer.endpoint, 0x80); + assert_eq!(transfer.timeout, 1000); + assert_eq!( + transfer.transfer_type, + ffi::constants::LIBUSB_TRANSFER_TYPE_BULK + ); + assert_eq!(transfer.buffer, buf.as_mut_ptr()); + assert_eq!(transfer.length, buf.len() as libc::c_int); +} diff --git a/rust_backend/vendor/rustix-1.1.4/.cargo_vcs_info.json b/rust_backend/vendor/rustix-1.1.4/.cargo_vcs_info.json deleted file mode 100644 index 3829c9e1..00000000 --- a/rust_backend/vendor/rustix-1.1.4/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "c4caf5caaa7e93828a2e4a4cdba1dd0171e45717" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/rust_backend/vendor/rustix-1.1.4/src/ffi.rs b/rust_backend/vendor/rustix-1.1.4/src/ffi.rs deleted file mode 100644 index 073b6b12..00000000 --- a/rust_backend/vendor/rustix-1.1.4/src/ffi.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Utilities related to FFI bindings. - -// If we have std, use it. -#[cfg(not(windows))] -#[cfg(feature = "std")] -pub use std::ffi::{CStr, CString, FromBytesWithNulError, NulError}; -#[cfg(feature = "std")] -pub use std::os::raw::{ - c_char, c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, -}; - -// If we don't have std, we can depend on core and alloc having these features -// in Rust 1.64+. -#[cfg(not(windows))] -#[cfg(all(feature = "alloc", not(feature = "std")))] -pub use alloc::ffi::{CString, NulError}; -#[cfg(not(feature = "std"))] -pub use core::ffi::{ - c_char, c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, -}; -#[cfg(not(windows))] -#[cfg(not(feature = "std"))] -pub use core::ffi::{CStr, FromBytesWithNulError}; diff --git a/rust_backend/vendor/rustix-1.1.4/src/static_assertions.rs b/rust_backend/vendor/rustix-1.1.4/src/static_assertions.rs deleted file mode 100644 index 3d071181..00000000 --- a/rust_backend/vendor/rustix-1.1.4/src/static_assertions.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Workarounds for Rust 1.63 where some things in the `static_assertions` -//! crate do things that don't work in const contexts. We want to call them in -//! const contexts in Rust versions where that's supported so that problems are -//! caught at compile time, and fall back to dynamic asserts in Rust 1.63. - -#![allow(unused_macros)] - -macro_rules! assert_eq_size { - ($x:ty, $y:ty) => { - assert_eq!(core::mem::size_of::<$x>(), core::mem::size_of::<$y>()); - }; -} - -macro_rules! assert_eq_align { - ($x:ty, $y:ty) => { - assert_eq!(core::mem::align_of::<$x>(), core::mem::align_of::<$y>()); - }; -} - -macro_rules! const_assert_eq { - ($x:expr, $y:expr) => { - assert_eq!($x, $y); - }; -} - -macro_rules! const_assert_ne { - ($x:expr, $y:expr) => { - assert_ne!($x, $y); - }; -} - -macro_rules! const_assert { - ($x:expr) => { - assert!($x); - }; -} diff --git a/rust_backend/vendor/rustix-1.1.4/.cargo-checksum.json b/rust_backend/vendor/rustix-1.1.5/.cargo-checksum.json similarity index 74% rename from rust_backend/vendor/rustix-1.1.4/.cargo-checksum.json rename to rust_backend/vendor/rustix-1.1.5/.cargo-checksum.json index 14b159e2..8922b166 100644 --- a/rust_backend/vendor/rustix-1.1.4/.cargo-checksum.json +++ b/rust_backend/vendor/rustix-1.1.5/.cargo-checksum.json @@ -1 +1 @@ -{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"a4a3c3581f9874c0375276fc58bdad605866df834795fa430ce493da48aa4778","CHANGES.md":"cb41541f3476ea8d2f1755e7fd14fb06b97c7e591b8cdf8571bd2c62d345af0a","CODE_OF_CONDUCT.md":"f210602311e3f74b32f46237fd55f4ce36d798e85e3db1432ec667f63a7ffc44","CONTRIBUTING.md":"63d06b36bf3e44bd905be68990004e4d6098870885c7d1019c56f847327e5a31","COPYRIGHT":"377c2e7c53250cc5905c0b0532d35973392af16ffb9596a41d99d202cf3617c9","Cargo.lock":"c062a8c6722b7b93c942b0b861a4e1138a7f97dedf5e2f6dc512221d0a81f54c","Cargo.toml":"13302af30f997a373ad50b7aa668d9612483e85be6b455e09d95bbd59fb5296f","Cargo.toml.orig":"219f712befc2057b71e7a5b156eaff40b7239dbb69966e8475542cbcd750cef7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-Apache-2.0_WITH_LLVM-exception":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","ORG_CODE_OF_CONDUCT.md":"a62b69bf86e605ee1bcbb2f0a12ba79e4cebb6983a7b6491949750aecc4f2178","README.md":"de51d792f8c9f28d1b9b364c31da9d2d5ca00166404d339850863875d0c21909","SECURITY.md":"4d75afb09dd28eb5982e3a1f768ee398d90204669ceef3240a16b31dcf04148a","benches/mod.rs":"9bcb9df020cb3d95db20dfcd549d5b912cdd4ca5a28de248a295001b5eeb47fe","build.rs":"74cb32e64aa6fe99c2496a425b016e22f4e43c438a8237966b8acae04a98eaf9","src/backend/libc/c.rs":"9ec8819d06a657fa202e6775950a938286618bc6cd2bd2e891b0998b6bcaba1c","src/backend/libc/conv.rs":"7a0d66156f7e26eedcc148694c2ce6e7c9c7f281e9fea3ee6bce3e23be11bb56","src/backend/libc/event/epoll.rs":"fabf21b7ab278a469fdc71d823c3414d3c72e24d72dab2bebe409105a93ad0e1","src/backend/libc/event/mod.rs":"554bcf6d614a99c32d021d0a2eb3018705f516120d4e2e52d4376cd835f3f28c","src/backend/libc/event/poll_fd.rs":"f65971706707fedbe1df2a5c1eafcb2d1a562f383a355e3bef87a905f306afbe","src/backend/libc/event/syscalls.rs":"4ec44f572ab04ad78b6776bb04ed0dc294fa85cd3d9eccad7c28e01dda90aec1","src/backend/libc/event/types.rs":"b966e0a3a018ca2ce507371be900fc73b2814cca450b784583207ad2df473f13","src/backend/libc/event/windows_syscalls.rs":"594ce6df675d1e318a876cb8a4c7f8787532c0f6c40305e3d2d8d3ab96dbdc29","src/backend/libc/fs/dir.rs":"a81037979ab8556c3628d506f310b4a051a0b4f5cb7e73d46ea67bd3738ea461","src/backend/libc/fs/inotify.rs":"69376ba2e81f98164029888d0cae554ca1702b532b0045dc55e282f1dd52d7bb","src/backend/libc/fs/makedev.rs":"e2baefed008497ace767da7b89a4513f23429d228b251ef70bee42c821d4c111","src/backend/libc/fs/mod.rs":"cc85f3b47b4318094859d7077a829d8bf2285a985e02496213b68b345fdc1ba1","src/backend/libc/fs/syscalls.rs":"35ff1ef84e1ae46182e771e0fd4bb7f67f85311ba46e84e336def370bcf1217e","src/backend/libc/fs/types.rs":"ef5c4b9612ca1cdbfe6b547cd4e2d7193ea6d7ed9cd09e8cd19a81c45a2a0a39","src/backend/libc/io/errno.rs":"355f9e7c6e617e62494e97f0a5e03ebc341dc4b28b66b30d2a25ba54ee9fb451","src/backend/libc/io/mod.rs":"746647bd864e4ec7717925b6d176cebdb392b7d015070244cc48d92780351dd6","src/backend/libc/io/syscalls.rs":"00ff59d1dabbea998ed018b9d8c2fa6e507116019b9e3e43cc800a91f710b1fe","src/backend/libc/io/types.rs":"a966b120932df9533410143d926a0140002ecf41051cd82673d17a04bbe48f0b","src/backend/libc/io/windows_syscalls.rs":"dd9e7e10aa02ae1f83bf6dd4b22b0ea472389dce762815593a3994ca6567f396","src/backend/libc/io_uring/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/io_uring/syscalls.rs":"5221a498e6c396d7c4cb60168ce8459ebca7d6720349a426d8083c227c694212","src/backend/libc/mm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mm/syscalls.rs":"c04c61ad3d7f3b24d13f89144d7fa0a05658cea4763207f9250db4f9362de2fe","src/backend/libc/mm/types.rs":"5c50a3d7b971209ba62a536e31fa3c0613b58328c27e8722fbdba86f1b76b1a1","src/backend/libc/mod.rs":"92d16eed953b10d3a0eed59f1413352c6a1c64a2b75be2c8d20628c59e7da127","src/backend/libc/mount/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mount/syscalls.rs":"b1d167b33764c94fbf1bde891478a3d370c576b2080b3daacb0a4313d240d6a0","src/backend/libc/mount/types.rs":"06f95e29ce60a9913189313f6450852227a6d6c39f89885cb91d0c9b243344fe","src/backend/libc/net/addr.rs":"6372c483e8ef78cd85eb26a94f61cd1efb8d2be6665fc8e13de67e4c5b28871d","src/backend/libc/net/ext.rs":"cfce41130eb2e3dd04271e0aa2c1e2de9866397d7d2a59e917d26b784ea30df5","src/backend/libc/net/mod.rs":"43004d73e16ba3107c0da4651dc749230a43597ccefc24caf75950f9ef91a7cc","src/backend/libc/net/msghdr.rs":"a1c6f46c1348b5e7b3d1cc2c58096783f2f7392d1d1f6d994585183200ec3bf0","src/backend/libc/net/netdevice.rs":"7c439090f99cb603de555f5acb065814a3344c4897f916e022ad07b979ccf025","src/backend/libc/net/read_sockaddr.rs":"95b573a014bf819a63fbe82169a63057f2f860abc8246d55a0d874e3acf7423b","src/backend/libc/net/send_recv.rs":"b18c8f7ccdf4b5dceb9ae8fb8590fd041aba590a2b4985872a5ba7c054ff49de","src/backend/libc/net/sockopt.rs":"fe89a3ccb0d5a50898f4f4d8f7fd9a2a253866005ffbecb0bb1d4d2bbc380d16","src/backend/libc/net/syscalls.rs":"18904d6d9797cf6e5fc0ef65bf6689c77f3d35e2c77544257f51a6418792346f","src/backend/libc/net/write_sockaddr.rs":"7b5047a50ac2ca966c39922d1cdeead2d40faabe4c7da6bf2ae46bce3bb96547","src/backend/libc/param/auxv.rs":"4e508557d04f18eb59e0cdf128e0e87d020c5fd04e97427417bc9171ed45ca52","src/backend/libc/param/mod.rs":"5234b8f1bcb886cca6ea003d411d75eaeebe58deedd80e3441354bf46ed85d4d","src/backend/libc/pid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/pid/syscalls.rs":"49ea679b96c0741d048e82964038f9a931bc3cf3a0b59c7db3df89629b9c49e6","src/backend/libc/pipe/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/pipe/syscalls.rs":"8c05fa158cfbd12c51546a308558a0e80c2abf4e5dc4c4c882dc7d26f00bf213","src/backend/libc/pipe/types.rs":"ad78eebf29c5768c5d29f2696f4ccca767e007f97abcfebb49d97a2ec569224d","src/backend/libc/prctl/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/prctl/syscalls.rs":"8a2684f444a7555098dce2b92270d81cefdae902716c6e5d59bd7b0657e8a29d","src/backend/libc/process/mod.rs":"016edc4e95f692ba63ccb264a676fe6f433de2cba4005a9ad9875fa2be2eae1a","src/backend/libc/process/syscalls.rs":"1af37e474ef0e21ba5ba62c3d5bf0a9dae7448c114e045959aaa3abef82b1564","src/backend/libc/process/types.rs":"b4b0cbc6fe175955323e4b2b286a12615f56308128e1f5d0ba8da1113107253e","src/backend/libc/process/wait.rs":"6963b441ccaf1c38fd6353751ceed84dc6b34b266a7f18b985590ee58f009409","src/backend/libc/pty/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/pty/syscalls.rs":"dc45fe291f0dc171470dfd9b1d79fb23594d39646512196a02667473fdc90e84","src/backend/libc/rand/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/rand/syscalls.rs":"d0efbf075f0c398d7d8f1983e7c42fb8331e9d4c8a8699d41f84ee2f967822c1","src/backend/libc/rand/types.rs":"4eb0b4cdd0a9b089d1c9f6a25ad1ca97be28a38b7b07a705ec605b773f63f880","src/backend/libc/shm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/shm/syscalls.rs":"e87bc9091178a1f582c5c7da612c6a227ee51cf171c458c358e9c6e9f4498c66","src/backend/libc/shm/types.rs":"0a1ac6bb9c024d94eb4bab7452937b7befcb30c581dc787b805011407ba1442d","src/backend/libc/system/mod.rs":"38563ea68829ca5a4b1b0695ac8a5c05718e85bdc88a36dc805efdfce45d3909","src/backend/libc/system/syscalls.rs":"3e066fef878b95b487fe2f6d8214abff95fc238477e94d4f97f3b440465c520e","src/backend/libc/system/types.rs":"6871e16aee14fe2ae03cea798c3e509ffe44778a9c0e5608fd73e2e015876d7e","src/backend/libc/termios/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/termios/syscalls.rs":"a7b5cb9755f3fdd70c031fdd9fb2764df63b2be5782457e642e9d82a3d0f62cd","src/backend/libc/termios/types.rs":"4204ca5e4d45c9788291cdb77a1e3977d33749350fb265f5fdccefc3bd30c51a","src/backend/libc/thread/cpu_set.rs":"5fca2b47289a2bb6ba908c910a6d27ac78ac10ea21d3c0c018f72f8816c5b9d7","src/backend/libc/thread/futex.rs":"0c7178bcc9dfd051908e0a0d993892c551c9699f1955dbf2bb003c2ef114ba25","src/backend/libc/thread/mod.rs":"b7f7961d7f6bf59d8a4773627d95aaedd6136402fe0e112673db16463a015414","src/backend/libc/thread/syscalls.rs":"48018a87ad253d21befb43b3d01464e301dc7e8ff2d67b7ec363f6d9004ac4c0","src/backend/libc/thread/types.rs":"81327c2df8439d1b2f6a04dd5c3985b4aefdd137c2f2f0a2c6c2b346112b08f1","src/backend/libc/time/mod.rs":"38563ea68829ca5a4b1b0695ac8a5c05718e85bdc88a36dc805efdfce45d3909","src/backend/libc/time/syscalls.rs":"fd72bec17484f773b2d0a6d07ade470f849493f9d694a39c49df8665ed92e908","src/backend/libc/time/types.rs":"437c77adfb87029e07fa3023207027077b3cdfe06ca29bdb8704d1be4113277b","src/backend/libc/ugid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/ugid/syscalls.rs":"c99a18b7699dc1888528c89ab4fe3b19a49e547de2d76da766c9af56ba9f2bfb","src/backend/libc/winsock_c.rs":"e766a63e1983b59f63a4ba06aec065241463deb3ca4d6989e8b1ce43870b0bfe","src/backend/linux_raw/arch/aarch64.rs":"12bb15064d25f0ed08e9eb8842c7dd570393222365b5a33206adc97406d2c061","src/backend/linux_raw/arch/arm.rs":"7cd435fa38fa553243e8d7c14294e90b948e9a2d21b5313e3b21493f3fb8f7e5","src/backend/linux_raw/arch/mips.rs":"4eb5e66070576b0978a7c0dc1d8f13fa29928cf022389cd66badcb7f3f5f014a","src/backend/linux_raw/arch/mips32r6.rs":"5a8a53e4af865ab1bfe2fe5c90f9c735d75fbbad49e59fc6e2b2436adc064797","src/backend/linux_raw/arch/mips64.rs":"a934ec7c25d59ea468196ca8b6c2b3bcf6068068f2a6ecfd1fa332905224580b","src/backend/linux_raw/arch/mips64r6.rs":"6bbe6e21e7fd4d88e0efd12f172ec25eb1ac78c7d50a509f302b2d3400aabff0","src/backend/linux_raw/arch/mod.rs":"bf50f92a5a34a6ad7abd7fbd691844bf0ab5b54a8cc4d8c51941c600f7d1b424","src/backend/linux_raw/arch/powerpc.rs":"30d33ca1e28e6e1a7745daa8e6063ff7eade5aea4468863fab3673bc006a3920","src/backend/linux_raw/arch/powerpc64.rs":"63074ee358ba97fcad955355b399b72e90390462139c57d6b3fddcf290592b72","src/backend/linux_raw/arch/riscv64.rs":"4cfeea0a45f5c43e26e2d6dd917f03489a2377bb88db619d9a921b0f4fc72655","src/backend/linux_raw/arch/s390x.rs":"797396e13e97feecb6eed9a9bc89017ab1faef381272ad013e144611681288d9","src/backend/linux_raw/arch/thumb.rs":"e5752ddd79820e783c6c8c977fca34e584908bc000e21ff5177872a7042c2d69","src/backend/linux_raw/arch/x86.rs":"c66e964b02413b1f9f843a845defb03b72f7c936dc1d35e53010870562bcd034","src/backend/linux_raw/arch/x86_64.rs":"823327d5f16bd33c586b2641817169d216f308d15a4b70eb3a77432623aa3b16","src/backend/linux_raw/c.rs":"1352edd3a4289765b485035604408c416ec653dab47cf1846759035845599970","src/backend/linux_raw/conv.rs":"1bd94a915370530ef5883c16b984680c7dacb0100a660408716f4b86d0f5ea79","src/backend/linux_raw/event/epoll.rs":"55a1b99c54c5c2cff85d852bc3511be78666341fcaa9050571ed196ec4bc4c5f","src/backend/linux_raw/event/mod.rs":"72e46b04637e2d1d2a6b97af616144995399e489d1fe916faf835d72fc8c64cd","src/backend/linux_raw/event/poll_fd.rs":"a5773464e9e30227b4046f029a27d3daf2c7bd0045e54565cf1a8ac2a0d61e81","src/backend/linux_raw/event/syscalls.rs":"81ec0bad4f30dffc7d484ff50a9456736206bc07e56a1d97283a53bc063442ec","src/backend/linux_raw/event/types.rs":"15365b0287ec3e76a20e3ecaf1dc3ed9cea0e72d6f299a4898db90f3ffb7c759","src/backend/linux_raw/fs/dir.rs":"93395b58420a9bcc9e8cfaf1d3c6664b6065f1f12175a2d015aaa40d3b95216f","src/backend/linux_raw/fs/inotify.rs":"3915ba464c134ef8b12bec297b1e40b0c36c657f241bd2e1861b7c39a3de5774","src/backend/linux_raw/fs/makedev.rs":"c6b4505c4bcbbc2460e80f3097eb15e2c8ef38d6c6e7abd78e39c53c372139e2","src/backend/linux_raw/fs/mod.rs":"3d3a42bbf4d086f806d7d9f6e4b0a49a873dc27f4f03ffca0921f5536acc24e5","src/backend/linux_raw/fs/syscalls.rs":"ae953d9db65865e04fa07b707376aa28e31ca8afb294c30b533af788bf2e0987","src/backend/linux_raw/fs/types.rs":"763b32ab67c25613f2cc1f753e6201f79ccd5098f6345e6c4d5529dbab39af4f","src/backend/linux_raw/io/errno.rs":"42bd687878c215730238bcce6a8d7d605f07cba36fd78a9f53ac88a5cd3a06c9","src/backend/linux_raw/io/mod.rs":"7ae2324427892cca6f5ab53858d847b165f790a72ec25f3d99fb15f0506c9f27","src/backend/linux_raw/io/syscalls.rs":"872548d81710cd5d27e4ddeaad91bca73b473bdead95738641388946db689693","src/backend/linux_raw/io/types.rs":"f08b224f483815ecd843d9aec9b0b4d5682409adee2c8e728ebbb874b71204a2","src/backend/linux_raw/io_uring/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/io_uring/syscalls.rs":"633c0b56aae827187a598be3b1717128ef33941953f969098884446fb5cbd8e4","src/backend/linux_raw/mm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/mm/syscalls.rs":"5f96a14759cdfb8d7dcca2551c863c6b17c1871a5de423c57d23813ab2ca8050","src/backend/linux_raw/mm/types.rs":"dd16b7121b72d3ea75bae6ed67daf3a3f6fe509ae9cc45f1b7d33285049b0e8f","src/backend/linux_raw/mod.rs":"8edbb969111d27b64a90c42307799e0a5cb1828a7bec3cba7cda67ea68b532d7","src/backend/linux_raw/mount/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/mount/syscalls.rs":"30d2a474e867e2453b401eb2e5e4a74b46ffc70efecf6c8cda16a20473a7c066","src/backend/linux_raw/mount/types.rs":"f393fbcfb762043bd4657852b58b9eaa5ace0585af8666c5234fbbd968de55bd","src/backend/linux_raw/net/addr.rs":"245f2e5302a1fb3d2d7deff8c8d5e32eab01d4b5679f351d32736737eabdf90a","src/backend/linux_raw/net/mod.rs":"5640bbaf4963ac4575b0537173422ed6f6a43f34a8f6b656e7257fbbb13e5bcb","src/backend/linux_raw/net/msghdr.rs":"6ac59d03090cd280664bb20887fd7d818bcfbc7d5beec7d791330ecbfe8bc070","src/backend/linux_raw/net/netdevice.rs":"905992885fa4c813a8bf2dcd5ab6092e760407b7026e484258a628285b5681d9","src/backend/linux_raw/net/read_sockaddr.rs":"ec8e5d4f1389e99884c668ad2530a8e4d2a9fa811690b41ea4ad3cfa9092749b","src/backend/linux_raw/net/send_recv.rs":"e1fe9b647b76c25138d462748e1e594e56ccb72e7dc02bd6c4ad3c94c3f24e64","src/backend/linux_raw/net/sockopt.rs":"e408b00e7e9819af77e9a4d3384d2b8203bb3351c7c653dc92f6f4d12b213a55","src/backend/linux_raw/net/syscalls.rs":"7ffe2da0380938b3e40ce5b9607b981d7f44fcf7705e12391e36dcd5257b825a","src/backend/linux_raw/net/write_sockaddr.rs":"e77f69f7535c7c9f8e7fc8fdfc60f208039cd681aa2c97b102edc53092f1eb43","src/backend/linux_raw/param/auxv.rs":"ec3d2fd93c10a36c5e634422d307efda71832ed8f680e53f19cc2882eced5208","src/backend/linux_raw/param/init.rs":"e442eb702dac5c550228196e890d1dd909cd8c2312e58e526d99cd031616466d","src/backend/linux_raw/param/libc_auxv.rs":"a06e2691ee174a81b8ca8d0c266c4a45512a95f274587e3b98786cfd7d9bbde5","src/backend/linux_raw/param/mod.rs":"2e6a1a1c00351b9c88bd615aa923f71d76208df5626dd9bea03067f28f81dc31","src/backend/linux_raw/pid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/pid/syscalls.rs":"d0b5a911940a07bb333ed709e2ecda31965a1aa979689d788af2a063272b0f43","src/backend/linux_raw/pipe/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/pipe/syscalls.rs":"63ea3215bb837d354f30866c7e86a3fd9596bdc3eac6986193694865da99b163","src/backend/linux_raw/pipe/types.rs":"55abd34b363307e6102dfca51fbe9a6dbd42254af1b93e1089711fa19699bd8a","src/backend/linux_raw/prctl/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/prctl/syscalls.rs":"a18b224307e0d27bda9b7b85c75cd6c7ddfe39f5ce52efb7bb0bf0585b757343","src/backend/linux_raw/process/mod.rs":"6b021a8b81a0323b520d89a1566360c0525a58fb0860ce9dcd4b46ef3bcb2fc3","src/backend/linux_raw/process/syscalls.rs":"8e7c6d1c07fbbc1db1ef22f05bcd91ceb2c3e3cbe2c99a158d70139c457d065e","src/backend/linux_raw/process/types.rs":"47ecd87edcd9bcc8f45058610b268c994f3f21a218e9052789c554eabda8c4ba","src/backend/linux_raw/process/wait.rs":"c1c90edeab2179280f86aeca0bd1b7237e1558645c80d98d9febb19b55f4c910","src/backend/linux_raw/pty/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/pty/syscalls.rs":"ae09c4aecc0ae87b1ca58d82efc58007b9dddaae78460d615f48da19d1cd0f89","src/backend/linux_raw/rand/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/rand/syscalls.rs":"8a1f3ba411892718074e48219ac32f0bc017bc68ce99a85398d512c18d0c51d8","src/backend/linux_raw/rand/types.rs":"a21dbb1ab31eed6b59a57520be5a3c603c36d18ea74e67f7527f40835ba209a3","src/backend/linux_raw/reg.rs":"3e321750d91d77c835abd09b06b5851ebf3134d3affb3cce306e38329871bec4","src/backend/linux_raw/runtime/mod.rs":"b2cae8cce3822c3c92942f06ea0b68464040dcac33c6f0f7ee392c6269993347","src/backend/linux_raw/runtime/syscalls.rs":"25679940620395e8e8f67a214b573ecba0d8a01b3d91f909ab36a707f889246a","src/backend/linux_raw/runtime/tls.rs":"364d651b58e0169e22c67586661848d86daff38080a374bb86ce3300794f7e4e","src/backend/linux_raw/shm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/shm/syscalls.rs":"d47734bcaebe004c3b5f1329df75eafd687aaf4ad9d16857dd2e8b2ed7d9e417","src/backend/linux_raw/shm/types.rs":"d6e739cae9a438624d3eb295146f1356e6beb92a12b21904dc5cb3d5dd80c85e","src/backend/linux_raw/system/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/system/syscalls.rs":"94e9f219276dba7f71fe6512a6d2554d3fcf05af08d6376619220b302ddd007d","src/backend/linux_raw/system/types.rs":"fc32d2141cc2305b129bee28ab446a0ce0e0a64505caaef06e3482ea5ae40090","src/backend/linux_raw/termios/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/termios/syscalls.rs":"c1d42cfc87d65a737fb0c38c8e04bd7f25536bbaaf134050b5cd782985a3c4fd","src/backend/linux_raw/termios/types.rs":"37ae0356a50d62dac7c3575f6fea13a4daf8ef10c119673fc9d4d30ac0c36ab6","src/backend/linux_raw/thread/cpu_set.rs":"3a7223d540e20bd45b342bd2781756fa3b9d0d331d3007db91e55a1d65e3a571","src/backend/linux_raw/thread/futex.rs":"2b949694d9c6a2b6fccc6b4cee73673b9cb74903728beb2e88464ea0b3778c3c","src/backend/linux_raw/thread/mod.rs":"93ada3063d5c398901b2ad130797ba1b797ffd814c969780bd5c06cea8e7619c","src/backend/linux_raw/thread/syscalls.rs":"17f9dde80daa01096146f8654fdc3e7ab79f274fb41e885580642da1816d8d4d","src/backend/linux_raw/thread/types.rs":"cc7153e742eacb78acfed2c5977e38a18f21afc9bb6e05bdbd8803c2b7ba1670","src/backend/linux_raw/time/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/time/syscalls.rs":"66e3811ffdd3fd7f9ce679063e6ce80e755149b37ce437045a30d48cf4f67cdd","src/backend/linux_raw/time/types.rs":"430e7798c2332b72d45f9d430e96302211d7195c3b0810d213442bfa5d8295ee","src/backend/linux_raw/ugid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/ugid/syscalls.rs":"e77637de0015835039562661d3451afeebce0c7d019cf8e93b6572ee4f20f71c","src/backend/linux_raw/vdso.rs":"1ef85f02ba89e5afa5b9b574143ed58c447aed18c4e50eb861183cd0e5cbefb0","src/backend/linux_raw/vdso_wrappers.rs":"cf31e9520c46293e05f1cba001269d9b56db6e3114326ea132e095903a310e9a","src/bitcast.rs":"6b26f35379b3a39b0cf1458d8ac5d5d1bef28e901ef27f0e5248fbfb2d286216","src/buffer.rs":"d5ce44cfd30941fa25d7b46b8039c2e028abc8e9cf20bfbf7dc6ebf3e4b56097","src/check_types.rs":"a49699c99d942a1d54093b4db36007f24b9807f52e8af6249da4fe4cbe1fde30","src/clockid.rs":"d277107e621f60e8daad25fe3aa62cd797499a3b3ab300f8d58bb74ea6c46e47","src/cstr.rs":"5529b770668c0abc904e732e8024fa504d3310da8451687b37298f9fe5581b83","src/event/epoll.rs":"692bfb5224dbc6aecf912a8218c9841d2cc45134dab462738957e69b3ee98251","src/event/eventfd.rs":"81cbd08f7bdf40a6ce1ca692b63da1dc8ba925282990668d9d68f1203e839fa1","src/event/kqueue.rs":"e6022ca584cb0b453a4c4e03be353c97d2f64727278c366758899a0167d48776","src/event/mod.rs":"fe148bcf9de924f3de4e3416b35c8d43a6964c460b12dcd898b2cf2bdc219abf","src/event/pause.rs":"fee4b6daf3ed698e85cd6e2c5b9058ffd84e4d21f6f25b336417a9e1ff200ec4","src/event/poll.rs":"0b3244e1e9c4b2fd17d699ad3ee0af999807836980791e889942b7c710faaa05","src/event/port.rs":"4bfdf5632cf19dac24db34f2be9913c6d34cd7a57168949d7a531b96fc6e9e27","src/event/select.rs":"f0b35f6ba35a4446bcef3b109516a4f1ff6ad295d0d4b53b5a9406aa06079b6d","src/ffi.rs":"fdf74f38eee3d4909b0a866748a6fd9c1a32948a71377c4f1579fc4448c057d8","src/fs/abs.rs":"393c537e13cd1436b88fdc8fe124f5cfa4f53d7914c9d291fb265fe2afbede02","src/fs/at.rs":"2758c9b1146d29da0fc94601b2b22776ec30bd00363aefd13a123ec513999f3c","src/fs/constants.rs":"832bd9afdb994620289c6ea352b3fca7e5a2525b4959f169fc44d5fc55c273ca","src/fs/copy_file_range.rs":"d3b644374390d482b2ff749a2459458872b57d0dcf9670368739b7833509a7c2","src/fs/dir.rs":"347a52f4ca9ac6321c52e802e97ec90d1b4c62ec955c8996fc17f8f5aed69966","src/fs/fadvise.rs":"33a4eba049ee331913feb8d740320abc58207a69cf02b81bb11291b83110ac5a","src/fs/fcntl.rs":"3cbcfeb62eca4818239a611bd2df35a0781e1a703b30860f15de77063c7e9179","src/fs/fcntl_apple.rs":"e2f23f038083621bcdecc98d02ce1023508afaecdb2ed0fba5c8b70f955301e5","src/fs/fcopyfile.rs":"ce565f61e1fbf2e31086077c2f1d01b6bb3b048915edda87fe9a4a4f5e8ff7e4","src/fs/fd.rs":"cdeddb69610f9bab80cefd74449dce842d7f1c16123958bbd7ea0a3520fdc7bc","src/fs/getpath.rs":"28f6970fc1bbc37bb35c84724b59eac436ea7407a4522e18c2bdacb1fdd2edd9","src/fs/id.rs":"1b5c8a8baf9a9bb1f895f97189cea4e5982a0d35b192afeec6340a6c6222e0cb","src/fs/inotify.rs":"160e05e6f9ce9e966754747d9ac77882228c572860560de95add337cab9caaa2","src/fs/ioctl.rs":"e348aa5f100a51b0181a885ef90e7a8fb558ea35363d09f4fe3989b101054335","src/fs/makedev.rs":"b31f8e58cb1098abd0e7e5e78c7ae00d7760cf94e1a30aafe04fd8f3707c8bbd","src/fs/memfd_create.rs":"b83a5ce6476a226c2098774020481d0dc0ec5c52be09d9a58a877437592e8928","src/fs/mod.rs":"4bc044b3db0a6b88af7dd5cb4726b72277ef980a90b64da266a9f181dce01514","src/fs/openat2.rs":"8e2b73aa55bb5a6fa5c74fd022133ec5577a1db2dc3ab0f2b022b44cd08cbf4e","src/fs/raw_dir.rs":"88b387dc5701f35d3ce32b0a2d36c8257bfaff1eb5336e01224df88749a93d24","src/fs/seek_from.rs":"f3f5bd18d27808f6be9ab2df4d05515b2b5703e5f1f02e6d36f20507e20fe5cd","src/fs/sendfile.rs":"b0fe446ad86ce44d4fc4b736d819478d71947a4ac27982864174ea16e990a839","src/fs/special.rs":"4fb09337169a48a2f1032487885f68acddf10d130ada2117e3027eb0846e1c12","src/fs/statx.rs":"49023d382193d3041c330b5ef54e6cefe8c1f90280134d141a6f7d4274c43ea4","src/fs/sync.rs":"84f24de0ea53f77bd5c471e097abcbecd19c4843b69607f9be9030eb7c9d5aa5","src/fs/xattr.rs":"a1eacfad1cd089fc1786367211870326e4077b452bf4d6c9b7d3e2e959d484de","src/io/close.rs":"e6f184a806e7671d03c61f09c24a721d410a177c4ac1e08f5039e21fa9caaf5c","src/io/dup.rs":"08926bbe2b2c5dbc3d0ff79bfac8be725b90106c91087c8db9df3928ad05c7c1","src/io/errno.rs":"d014b6a4a4d92cda1e75873a074226101b58bccb4b787985ab00517a3dd9b926","src/io/fcntl.rs":"99fa698c6a6c68ecece7e7d14e0a65e1b6e17d360f028e14f8800d42a355b22c","src/io/ioctl.rs":"56d0fb4f4ab72ca4aca6553d79263be19dedc46a575e61241614404af910fd9e","src/io/mod.rs":"8656db3ce0fc51128d9bc6daac4591a5fedd269333627ebf86d6f045305f9e0b","src/io/read_write.rs":"84930810319605109d5d0d592f1470213ab7d009e1f30b49ee4e2de71afe8ba9","src/io_uring/bindgen_types.rs":"0f38cb903c962abe2fca743e137d191644428582c5e48ca6004f4e3b5bd11631","src/io_uring/mod.rs":"47b88058e5db24befc72e384558751ed40f927924875ee91099d2e35c120ec1f","src/ioctl/bsd.rs":"614e647caad5d7f81b2d570ca0dc77cd942f53a2544e4d1252155370e6fe364a","src/ioctl/linux.rs":"8b78fd0d1b2c078a25a49b7e85d8b2c03c5258c57662b3e3bdd8b7999a685a61","src/ioctl/mod.rs":"430acf575ad2eb49ee300efd1630b55cca1ef7197be7a0c0d4f2fd7feb80f791","src/ioctl/patterns.rs":"8bc7f5020c68ef67667b9078a867eb9bc5ad6ab7c674c577541d360052a299d3","src/kernel_sigset.rs":"f457d0e4b6b10c458a84b4877599f9f4607e6b4693dbc37b80f64a269711f686","src/lib.rs":"e7b621db482e374c751adf4fd092af9c5ebea7e0e9cb15f7afe25266b8156963","src/maybe_polyfill/no_std/io/mod.rs":"77889bb5c5a4f2e50e38379cdaa5d0fef4b0cafc3da056735df01f6deae75747","src/maybe_polyfill/no_std/mod.rs":"2a10587d0753bd4f849056643fefc98cea90dce266396f8a7bb3a3a1f318e0bb","src/maybe_polyfill/no_std/net/mod.rs":"1e94d21d455bb1da18a4fccb24b3d93ad42698e22dd888b78115d6266c80288a","src/maybe_polyfill/no_std/os/fd/mod.rs":"27ef0afbcb0695cbb15101070f417eb51e0ef85ae66ec967d95e80771d507c47","src/maybe_polyfill/no_std/os/fd/owned.rs":"7ccc8373b44b9ded0e5f4ac9f3c57cac089cc78ce756fc6c13be4d848306329b","src/maybe_polyfill/no_std/os/fd/raw.rs":"f3648c7bd4a6ff94bd823ed9e0d99d398e02f24875cf9b25962736999e7c6943","src/maybe_polyfill/no_std/os/mod.rs":"27dab639a765827644005d5f2fcc7c825310606b889cc8dd83f54c9528350dc0","src/maybe_polyfill/no_std/os/windows/io/mod.rs":"5bbcc05c83fee5026dd744a994e0458469466d5be39081baa62df07753b92fd2","src/maybe_polyfill/no_std/os/windows/io/raw.rs":"0a82257f263f02a685536538b656f152041ab406155feab294e93e00969b834f","src/maybe_polyfill/no_std/os/windows/io/socket.rs":"f455fbe9f5dfb38c77892d39116d0153c0e68aff35c0967ec8a28a9587cdecfa","src/maybe_polyfill/no_std/os/windows/mod.rs":"fdb416f8f231a4e778b5f985b9ae712ece5e1a1402963ad1a5f6a8b9843795f4","src/maybe_polyfill/std/mod.rs":"d0261b88ece984c9947896a89d7f3ff3470603e5bb5681169ebdb56cbd98a9a6","src/mm/madvise.rs":"092ff2064b16960369a908aeed70ec38a16fa2a0207f814cdea379e2f2286954","src/mm/mmap.rs":"7a19699f3b30639f8796cd762b836e74e9aa0950960bb818b1eabdb56fce40f7","src/mm/mod.rs":"b3a6cb838986d45825b912355cedead761211a494ca6f89b2367a2d2157e340e","src/mm/msync.rs":"0cfb895703b8b7bac47426c8f03499f2aced3f4e1f31008243a1a629e9d44a1d","src/mm/userfaultfd.rs":"4d72aee1ba5b6f079064370dcb4fb3a90b359971c869ab8d9727c359f61b971a","src/mount/fsopen.rs":"4a481a5fc1f6eea237a895de76a87edf97e29fc2f3a847fdaaa8d5254acbc71f","src/mount/mod.rs":"3896d1f0059a3c26314af70df27e3a9445c0f21843cd33589850e6f0e186f773","src/mount/mount_unmount.rs":"b888da0c4b59a4552d9058067a357aac9d6f8ccf3c5b2b8d735fb9590b1e9cf0","src/mount/types.rs":"601ae3e10b7dc496fed7f3b40a80e81c6edd7bf13189d7be45c3212d4c684c39","src/msan.rs":"da3e2f18b8e58dc9e4e32cb4bb81dd69bc7026b46d946d46ccb7623a5f89e076","src/net/addr.rs":"24b5214b3abc4c5459aa23e46e835edd8b61f9caf54afe43abf4361a9bf0f829","src/net/mod.rs":"148286bb768ac38e74716bc8aa3fbdc446bb73b2e525524df7b31ed5bb5d1c9b","src/net/netdevice.rs":"1b66af69cedab4be6eac7e8fca0406f880ba5b24d4031b42a473d0b8a2cc7fd2","src/net/send_recv/mod.rs":"8271eb7b5dd292ed46324d8e188e990f47bbdb20a35ca48905c0cf3f7fd3a899","src/net/send_recv/msg.rs":"d44b02ee8275f12c32ee67508aaf88d4d34954ebee037bfe7168527c98ebd0bb","src/net/socket.rs":"89e97e4675396cd09954729370f02e74ebb1433800e07ac3702cf0bf39a5b661","src/net/socket_addr_any.rs":"2723feb96ecffeaa552c21adc63eec887549a28b9748088995e68a053f29b59e","src/net/socketpair.rs":"aa9013de19945bfea3f7d2889cc23abd384717b79f38cd5d045dec09c7517658","src/net/sockopt.rs":"8333065c208f5e91fcac9a737a64afc93486bfb25a447a6540a78bcd5fa2ff64","src/net/types.rs":"a2f5e62229fc3d38bae6bd29815a9a985a11b1a133616a3d52f936f24bc17adc","src/net/wsa.rs":"29856e6d0108be6b719f6ef973c5396e48ddd58ecd0c2db9ca3e2e0ba4eccb5b","src/not_implemented.rs":"bf526ecc1872186d5fb57dc186c7d156895cd447ae836b44326c0ba475b6bbee","src/param/auxv.rs":"a0239a69a7b2484674f8f556212b45a0f4d3bafbccc23f4cd8bc9ae1fb2883e1","src/param/init.rs":"f44c8dcf26c3d5f8d8b5aa0a7ee5d6e953ad4baeaca88c6f20cb88eaf0f2e3c9","src/param/mod.rs":"c514216b96d9520c9ffc5662c97b24f1f36f56fe1f5f3c9f64175c8b35577288","src/path/arg.rs":"bafbc54acc499e0d0602eb4c15bc4f781f402443742f4346355c8a160df6fc2d","src/path/dec_int.rs":"9a8d20552acb530d69593f8bee5a6dca2c96a9677e04c7925ad7dd2e23cedf08","src/path/mod.rs":"8f4e2cb48cbdbf6844f25232e5a19fcfe22048b5eb2134a1367349a44f88e8ea","src/pid.rs":"9f167f9f9c73e63b3802db12a7b5ddb824adbb7c357de8b28396f469b8b788c3","src/pipe.rs":"ba3618c6e6cedceedb33f96cbc45d185291e1c1751cee0d5652bd44d9e05188d","src/prctl.rs":"86c893026784a698532323a8f05acdab9452b686f62d98de0a7507988dfdf96a","src/process/chdir.rs":"d5ed5d97e1c7a10c5e935f24774d42fbe4f0253620709e86f9e8bff1c8a93b66","src/process/chroot.rs":"01191eeb86295975874019dc675cb0f8f0ca5f79dd78dc9d1c791a952adb3512","src/process/exit.rs":"e8c22fd82e85652eb3ff892ee421d43d8edfa82729815417bd8e1624f254c9c1","src/process/fcntl_getlk.rs":"7fa3ddc36cee2547d49a107dc313ac438b5fde7ba6e9bcb741c549ac151b4364","src/process/id.rs":"71d82dcce9d3cad4bf4cbfa343be688146829e8d111934e16b5766e066ead36a","src/process/ioctl.rs":"0dc8cf8a8fddb710f14f695f1f253c5b474d9aa5691853d7142c1bc6c0a0a1b1","src/process/kill.rs":"7d6d2b15f00c2d0dd08042a1a4498a5080a3a159183ea630cd265283040a6f90","src/process/mod.rs":"9c6d36d7a8c78c86919a4516108154f40c6088e18e7eed76c926d9be27daa1db","src/process/pidfd.rs":"ce9c52d7f8a3d79a285f070a2a8c0536a84a7e31a8dcb0d76def1078ed6f0726","src/process/pidfd_getfd.rs":"88bfa535b25d8983c5d0fe0d5328da31252da7218d96602260739eeb5689529d","src/process/pivot_root.rs":"aeb2464464efd81ca0b940e3e5e3ed1b09380f3e6ba3200972e0ba100f78badc","src/process/prctl.rs":"493da650e869e098c34390c2e9b9be8dfd7cfdfdf0139bbdae1f86639c5d9e92","src/process/priority.rs":"f11cf35ed9eee03a1f84120754e76db51cf64e6a0d609c9c7fc4b373b9eae5fe","src/process/procctl.rs":"70e4b4a7de295eeefc5d595b3829bf99e6bec5fbc04eaefa897eefd9cac12d3f","src/process/rlimit.rs":"10becde11d8b5d3b50aeb21a8fcdf53ec7803bdbbf8d1f4233b8e6c0b85fafb1","src/process/types.rs":"9358379e9f5f992cb0cf6e0e71445d9a619ed2827e29b525e0e3c5fec81cfa95","src/process/umask.rs":"15841f5e178dd032f8b8def8545c9e98b205e6b1ba70bf9c07c2c0327a8999c0","src/process/wait.rs":"6e9763df5e56390c55aa8028851f20391b104e93016fa0acacaf6063063576f6","src/pty.rs":"8ccdcd92b4c7a452da1068b44b196836f694fa46fa22c446a7f9e1d1dc4172c7","src/rand/getrandom.rs":"50725a6edf19952a33771c8dd671d15feeb2798362291fbc3ca9758110b36e67","src/rand/mod.rs":"cab59332aadd9b679f5b22cbb222d48ee028af5eb9fd4a4d43922da659b895d7","src/runtime.rs":"97c4d285b531af32b40fa11309c9b04fb997c8ae6c89fda91897b6d555ef936e","src/shm.rs":"8c80096f1a619c43ee078d947129d0dc0ba7c241496335c7a12c91fbc4ddf1c7","src/signal.rs":"666c0678a2da8bb309fade625016982c7dbe4313da9d74dfed99eac5d8c50a7c","src/static_assertions.rs":"504cf66f0d5b8e335be02f9ae8b0a355abc98c2c82eec0098bdf0988e662e260","src/stdio.rs":"38fff53c9845a0e44742b2c4d1d13632b88f7e53ae8888405a645fdf758c05f6","src/system.rs":"388b4a57c50b41b56ddd3e1968d9f505a213dfe65602e61d2916389f065389d4","src/termios/ioctl.rs":"8621e7525142d604d7e1bd242132b6fdb5dad048040bcf352acf5213aeb75ef6","src/termios/mod.rs":"1c467add8298266b9a931d76be51a1046e2fd541775e0856f2700c436ef37da6","src/termios/tc.rs":"34a9ba54a0207aad1aa5be7adff79708e91623e3bc5a1a2cac756d66519deafe","src/termios/tty.rs":"25438c7e49621fa64b2777bd7b4de6e74bd9bd1aff324f99daf8619f98c8d013","src/termios/types.rs":"06272f111388c7f3db459a2e4acb107b0ffc50227257eddfa929a29b5a8ef991","src/thread/clock.rs":"e90a1437e5f9a0decce312c3fa0259d100ec85e050cf14a497c6c26a3bd7d32e","src/thread/futex.rs":"5a2919d7e9b161d65c09b6f38e702e1a5772ee5f08295e761c75c344707f8a1a","src/thread/id.rs":"00de3389f3291c53ad5cddbecea34d31d8925a6c53c475a7cb5c0c0acac97492","src/thread/libcap.rs":"73f233865e06180aedea39082a5cf36541147d9faad37b7efc3707bf9de67461","src/thread/membarrier.rs":"0c71af8f563d3e2c3552e220dbf66a31d4c35cfea9fd900d60fe002bb230baea","src/thread/mod.rs":"f6ea8b17a9d465c2fb0e015ac37dd41625bcd085022c39b115ffd96e1c235e44","src/thread/prctl.rs":"ebc63d13874b10d93a1c55868e965638192a1184f0d894502ffad2f1623ca549","src/thread/sched.rs":"d1eb4de567dfd1471293b56a275d1003390d7a56a689ed30bbb3c68ebab3fa73","src/thread/sched_yield.rs":"4208a36afd20e36c9551cf62ea5facaccfed2fffac85aaa66240b777b8b28de0","src/thread/setns.rs":"dd7cf5423ab6c362edea4b45b9c93066dd126ecf950c07f4a815fe26cf89a99e","src/time/clock.rs":"7588a33f5279ee683f39ce72a8d0a8dfcc2e25d3dbc29990e6829d7c2df7d99b","src/time/mod.rs":"d76112ee6510e209410025cb0b13623906577793f6c172f64e59ea5a90218801","src/time/timerfd.rs":"69fdedec9b22dbc542dfbc55a92ad59423001311067e1398fbab49f02cb29c8f","src/timespec.rs":"0e2c282111bc2e7cfccfe5381ab55995a9ef14c2ad063277dd77b6b54eec8087","src/ugid.rs":"2b44db5ba5f3f2832226eff537cafd54d748eaa03e2a96b403409f33bfdb1147","src/utils.rs":"ad680ad8564cb7b37fc746e0b3f89d389278fe1b294ef343a5c4a37677414065","src/weak.rs":"45b1e4ab84e6a1e121f61084ec88fdb9b0d2e870df6d415349c88fabecd5f7f5"},"package":"b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"} \ No newline at end of file +{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"d766d6d4817f42bf7f7b9bcee48065d461050af04e1042a6b56d67eba21c6eeb","CHANGES.md":"cb41541f3476ea8d2f1755e7fd14fb06b97c7e591b8cdf8571bd2c62d345af0a","CODE_OF_CONDUCT.md":"f210602311e3f74b32f46237fd55f4ce36d798e85e3db1432ec667f63a7ffc44","CONTRIBUTING.md":"63d06b36bf3e44bd905be68990004e4d6098870885c7d1019c56f847327e5a31","COPYRIGHT":"377c2e7c53250cc5905c0b0532d35973392af16ffb9596a41d99d202cf3617c9","Cargo.lock":"122912902e62afe1f44a2987a7410f5fdebadec62e46236709d23b81e417d11a","Cargo.toml":"f57824af546c008210d8d6d63bc0fe866a3887d06100469448de3432ffa108ec","Cargo.toml.orig":"1e162d556c992b8bd5c4d4d7256f6783d95be1e75143edeaa43c92055a57bc8a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-Apache-2.0_WITH_LLVM-exception":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","ORG_CODE_OF_CONDUCT.md":"a62b69bf86e605ee1bcbb2f0a12ba79e4cebb6983a7b6491949750aecc4f2178","README.md":"85cc3e1acc8af785b0becb2289af510bbb158ccefdeefa5e390990d44b47d52e","SECURITY.md":"4d75afb09dd28eb5982e3a1f768ee398d90204669ceef3240a16b31dcf04148a","benches/mod.rs":"9bcb9df020cb3d95db20dfcd549d5b912cdd4ca5a28de248a295001b5eeb47fe","build.rs":"f7609b919173e7121493266d0f89db724a1e47bb105ebd05397f869639603eec","src/backend/libc/c.rs":"cf5366ecc60a20707f271b4b9ecfbd6b4902541a465d9b715f7dfe4e42ea75ea","src/backend/libc/conv.rs":"fbcf3bc1ce6a6d728f139ab470016ab5eb90c64cefa1ec03c8252e33c1747ffe","src/backend/libc/event/epoll.rs":"fabf21b7ab278a469fdc71d823c3414d3c72e24d72dab2bebe409105a93ad0e1","src/backend/libc/event/mod.rs":"554bcf6d614a99c32d021d0a2eb3018705f516120d4e2e52d4376cd835f3f28c","src/backend/libc/event/poll_fd.rs":"fd1106d839022b53da754adf3ecf2f177c9890a8f01a424f83dcbf6c4962ca8b","src/backend/libc/event/syscalls.rs":"a0dca8e2a44b34959d5adf29eaedc2d529ff8dc6d34fcd604a8b9607fb43485c","src/backend/libc/event/types.rs":"b966e0a3a018ca2ce507371be900fc73b2814cca450b784583207ad2df473f13","src/backend/libc/event/windows_syscalls.rs":"35ec291f2df23cf8de8ddbd853a8c7f8fb242ee57e01737f666bea110b2ab305","src/backend/libc/fs/dir.rs":"48822229307a4d7ea86866f3307708105f42780b9f3950aa3b68212d6d2a56ca","src/backend/libc/fs/inotify.rs":"69376ba2e81f98164029888d0cae554ca1702b532b0045dc55e282f1dd52d7bb","src/backend/libc/fs/makedev.rs":"e2baefed008497ace767da7b89a4513f23429d228b251ef70bee42c821d4c111","src/backend/libc/fs/mod.rs":"377b8f10c6b3afc2413026c6fdff17f7afc0948dfd1c926e7a3fa7c3b15749da","src/backend/libc/fs/syscalls.rs":"b1281904b2746cef06bab82a1ee0d7465add6387d47d87b403413b58519f09f6","src/backend/libc/fs/types.rs":"354d2ffe66965986c6062efd8bf630a6f6f3710e690ccd773b5c2199719602ab","src/backend/libc/io/errno.rs":"355f9e7c6e617e62494e97f0a5e03ebc341dc4b28b66b30d2a25ba54ee9fb451","src/backend/libc/io/mod.rs":"746647bd864e4ec7717925b6d176cebdb392b7d015070244cc48d92780351dd6","src/backend/libc/io/syscalls.rs":"d04c8b7b25ef9c1ab41b7180b7de89b1780b3f36f4bc471a6b5aa0475b02a715","src/backend/libc/io/types.rs":"a966b120932df9533410143d926a0140002ecf41051cd82673d17a04bbe48f0b","src/backend/libc/io/windows_syscalls.rs":"dd9e7e10aa02ae1f83bf6dd4b22b0ea472389dce762815593a3994ca6567f396","src/backend/libc/io_uring/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/io_uring/syscalls.rs":"5221a498e6c396d7c4cb60168ce8459ebca7d6720349a426d8083c227c694212","src/backend/libc/mm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mm/syscalls.rs":"c04c61ad3d7f3b24d13f89144d7fa0a05658cea4763207f9250db4f9362de2fe","src/backend/libc/mm/types.rs":"5c50a3d7b971209ba62a536e31fa3c0613b58328c27e8722fbdba86f1b76b1a1","src/backend/libc/mod.rs":"92d16eed953b10d3a0eed59f1413352c6a1c64a2b75be2c8d20628c59e7da127","src/backend/libc/mount/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mount/syscalls.rs":"b1d167b33764c94fbf1bde891478a3d370c576b2080b3daacb0a4313d240d6a0","src/backend/libc/mount/types.rs":"06f95e29ce60a9913189313f6450852227a6d6c39f89885cb91d0c9b243344fe","src/backend/libc/net/addr.rs":"5faa13fdac91839ab32b111b1004ddd09a4ab455ddb5d59bd1263874381b58b3","src/backend/libc/net/ext.rs":"cfce41130eb2e3dd04271e0aa2c1e2de9866397d7d2a59e917d26b784ea30df5","src/backend/libc/net/mod.rs":"43004d73e16ba3107c0da4651dc749230a43597ccefc24caf75950f9ef91a7cc","src/backend/libc/net/msghdr.rs":"a1c6f46c1348b5e7b3d1cc2c58096783f2f7392d1d1f6d994585183200ec3bf0","src/backend/libc/net/netdevice.rs":"7c439090f99cb603de555f5acb065814a3344c4897f916e022ad07b979ccf025","src/backend/libc/net/read_sockaddr.rs":"95b573a014bf819a63fbe82169a63057f2f860abc8246d55a0d874e3acf7423b","src/backend/libc/net/send_recv.rs":"b18c8f7ccdf4b5dceb9ae8fb8590fd041aba590a2b4985872a5ba7c054ff49de","src/backend/libc/net/sockopt.rs":"06026cd64ec3f30c7cdb2d7f7081c0f78d07f0eb8613cc2d8041ee3deb097aa8","src/backend/libc/net/syscalls.rs":"18904d6d9797cf6e5fc0ef65bf6689c77f3d35e2c77544257f51a6418792346f","src/backend/libc/net/write_sockaddr.rs":"7b5047a50ac2ca966c39922d1cdeead2d40faabe4c7da6bf2ae46bce3bb96547","src/backend/libc/param/auxv.rs":"4e508557d04f18eb59e0cdf128e0e87d020c5fd04e97427417bc9171ed45ca52","src/backend/libc/param/mod.rs":"5234b8f1bcb886cca6ea003d411d75eaeebe58deedd80e3441354bf46ed85d4d","src/backend/libc/pid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/pid/syscalls.rs":"49ea679b96c0741d048e82964038f9a931bc3cf3a0b59c7db3df89629b9c49e6","src/backend/libc/pipe/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/pipe/syscalls.rs":"3cc763ce6846c7d273c7aeae84f7e35691a8aa1a4fc71ebaa7fc803550ba58a6","src/backend/libc/pipe/types.rs":"18aedc2748f50e71fb2518cb35ec2ade88ccca9e2febe7f3b00f5003735140cb","src/backend/libc/prctl/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/prctl/syscalls.rs":"8a2684f444a7555098dce2b92270d81cefdae902716c6e5d59bd7b0657e8a29d","src/backend/libc/process/mod.rs":"016edc4e95f692ba63ccb264a676fe6f433de2cba4005a9ad9875fa2be2eae1a","src/backend/libc/process/syscalls.rs":"1f3c7e261fbcf5046bc34981ac217159ecf7992855d3d5cb3bd890a9c7d7d7eb","src/backend/libc/process/types.rs":"4aac2e0c22e5f45e4f25d39eada9d4bc3df79cfd9c70074f9055cb7ccfa70166","src/backend/libc/process/wait.rs":"6963b441ccaf1c38fd6353751ceed84dc6b34b266a7f18b985590ee58f009409","src/backend/libc/pty/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/pty/syscalls.rs":"dc45fe291f0dc171470dfd9b1d79fb23594d39646512196a02667473fdc90e84","src/backend/libc/rand/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/rand/syscalls.rs":"d0efbf075f0c398d7d8f1983e7c42fb8331e9d4c8a8699d41f84ee2f967822c1","src/backend/libc/rand/types.rs":"4eb0b4cdd0a9b089d1c9f6a25ad1ca97be28a38b7b07a705ec605b773f63f880","src/backend/libc/shm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/shm/syscalls.rs":"e87bc9091178a1f582c5c7da612c6a227ee51cf171c458c358e9c6e9f4498c66","src/backend/libc/shm/types.rs":"0a1ac6bb9c024d94eb4bab7452937b7befcb30c581dc787b805011407ba1442d","src/backend/libc/system/mod.rs":"38563ea68829ca5a4b1b0695ac8a5c05718e85bdc88a36dc805efdfce45d3909","src/backend/libc/system/syscalls.rs":"0fc4f585417243cb4c5c1df3c9c5c987b7ab195c93bcdbcbf7bd5a05b582ecd2","src/backend/libc/system/types.rs":"6871e16aee14fe2ae03cea798c3e509ffe44778a9c0e5608fd73e2e015876d7e","src/backend/libc/termios/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/termios/syscalls.rs":"a7b5cb9755f3fdd70c031fdd9fb2764df63b2be5782457e642e9d82a3d0f62cd","src/backend/libc/termios/types.rs":"4204ca5e4d45c9788291cdb77a1e3977d33749350fb265f5fdccefc3bd30c51a","src/backend/libc/thread/cpu_set.rs":"5fca2b47289a2bb6ba908c910a6d27ac78ac10ea21d3c0c018f72f8816c5b9d7","src/backend/libc/thread/futex.rs":"0c7178bcc9dfd051908e0a0d993892c551c9699f1955dbf2bb003c2ef114ba25","src/backend/libc/thread/mod.rs":"b7f7961d7f6bf59d8a4773627d95aaedd6136402fe0e112673db16463a015414","src/backend/libc/thread/syscalls.rs":"9bc50af501ab33c8dfeaab39fdd0c681c1576e5326d32885f77a14ebad5a3103","src/backend/libc/thread/types.rs":"81327c2df8439d1b2f6a04dd5c3985b4aefdd137c2f2f0a2c6c2b346112b08f1","src/backend/libc/time/mod.rs":"38563ea68829ca5a4b1b0695ac8a5c05718e85bdc88a36dc805efdfce45d3909","src/backend/libc/time/syscalls.rs":"fd72bec17484f773b2d0a6d07ade470f849493f9d694a39c49df8665ed92e908","src/backend/libc/time/types.rs":"733545e4ac8eb0904a2358d301df2f372909bd6ddc4f86ee38d241d0ff21e604","src/backend/libc/ugid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/ugid/syscalls.rs":"c99a18b7699dc1888528c89ab4fe3b19a49e547de2d76da766c9af56ba9f2bfb","src/backend/libc/winsock_c.rs":"e766a63e1983b59f63a4ba06aec065241463deb3ca4d6989e8b1ce43870b0bfe","src/backend/linux_raw/arch/aarch64.rs":"12bb15064d25f0ed08e9eb8842c7dd570393222365b5a33206adc97406d2c061","src/backend/linux_raw/arch/arm.rs":"7cd435fa38fa553243e8d7c14294e90b948e9a2d21b5313e3b21493f3fb8f7e5","src/backend/linux_raw/arch/mips.rs":"4eb5e66070576b0978a7c0dc1d8f13fa29928cf022389cd66badcb7f3f5f014a","src/backend/linux_raw/arch/mips32r6.rs":"5a8a53e4af865ab1bfe2fe5c90f9c735d75fbbad49e59fc6e2b2436adc064797","src/backend/linux_raw/arch/mips64.rs":"a934ec7c25d59ea468196ca8b6c2b3bcf6068068f2a6ecfd1fa332905224580b","src/backend/linux_raw/arch/mips64r6.rs":"6bbe6e21e7fd4d88e0efd12f172ec25eb1ac78c7d50a509f302b2d3400aabff0","src/backend/linux_raw/arch/mod.rs":"bf50f92a5a34a6ad7abd7fbd691844bf0ab5b54a8cc4d8c51941c600f7d1b424","src/backend/linux_raw/arch/powerpc.rs":"30d33ca1e28e6e1a7745daa8e6063ff7eade5aea4468863fab3673bc006a3920","src/backend/linux_raw/arch/powerpc64.rs":"63074ee358ba97fcad955355b399b72e90390462139c57d6b3fddcf290592b72","src/backend/linux_raw/arch/riscv64.rs":"4cfeea0a45f5c43e26e2d6dd917f03489a2377bb88db619d9a921b0f4fc72655","src/backend/linux_raw/arch/s390x.rs":"797396e13e97feecb6eed9a9bc89017ab1faef381272ad013e144611681288d9","src/backend/linux_raw/arch/thumb.rs":"e5752ddd79820e783c6c8c977fca34e584908bc000e21ff5177872a7042c2d69","src/backend/linux_raw/arch/x86.rs":"c66e964b02413b1f9f843a845defb03b72f7c936dc1d35e53010870562bcd034","src/backend/linux_raw/arch/x86_64.rs":"823327d5f16bd33c586b2641817169d216f308d15a4b70eb3a77432623aa3b16","src/backend/linux_raw/c.rs":"1352edd3a4289765b485035604408c416ec653dab47cf1846759035845599970","src/backend/linux_raw/conv.rs":"1bd94a915370530ef5883c16b984680c7dacb0100a660408716f4b86d0f5ea79","src/backend/linux_raw/event/epoll.rs":"55a1b99c54c5c2cff85d852bc3511be78666341fcaa9050571ed196ec4bc4c5f","src/backend/linux_raw/event/mod.rs":"72e46b04637e2d1d2a6b97af616144995399e489d1fe916faf835d72fc8c64cd","src/backend/linux_raw/event/poll_fd.rs":"a5773464e9e30227b4046f029a27d3daf2c7bd0045e54565cf1a8ac2a0d61e81","src/backend/linux_raw/event/syscalls.rs":"81ec0bad4f30dffc7d484ff50a9456736206bc07e56a1d97283a53bc063442ec","src/backend/linux_raw/event/types.rs":"15365b0287ec3e76a20e3ecaf1dc3ed9cea0e72d6f299a4898db90f3ffb7c759","src/backend/linux_raw/fs/dir.rs":"93395b58420a9bcc9e8cfaf1d3c6664b6065f1f12175a2d015aaa40d3b95216f","src/backend/linux_raw/fs/inotify.rs":"3915ba464c134ef8b12bec297b1e40b0c36c657f241bd2e1861b7c39a3de5774","src/backend/linux_raw/fs/makedev.rs":"c6b4505c4bcbbc2460e80f3097eb15e2c8ef38d6c6e7abd78e39c53c372139e2","src/backend/linux_raw/fs/mod.rs":"3d3a42bbf4d086f806d7d9f6e4b0a49a873dc27f4f03ffca0921f5536acc24e5","src/backend/linux_raw/fs/syscalls.rs":"b2d13695ec2f5248b3b030d6ccbdf054897e4ce01ec88b2f856d16d886d98840","src/backend/linux_raw/fs/types.rs":"2ceffce6b2e5aea7d0f81f374ffbdeae5a9086c1c07cbc2228ea07012b42817d","src/backend/linux_raw/io/errno.rs":"556a8b7df8c1de133871b61afa8db60298ab9b0164c4efebac1cc9f4834f21d9","src/backend/linux_raw/io/mod.rs":"7ae2324427892cca6f5ab53858d847b165f790a72ec25f3d99fb15f0506c9f27","src/backend/linux_raw/io/syscalls.rs":"872548d81710cd5d27e4ddeaad91bca73b473bdead95738641388946db689693","src/backend/linux_raw/io/types.rs":"f08b224f483815ecd843d9aec9b0b4d5682409adee2c8e728ebbb874b71204a2","src/backend/linux_raw/io_uring/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/io_uring/syscalls.rs":"633c0b56aae827187a598be3b1717128ef33941953f969098884446fb5cbd8e4","src/backend/linux_raw/mm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/mm/syscalls.rs":"5f96a14759cdfb8d7dcca2551c863c6b17c1871a5de423c57d23813ab2ca8050","src/backend/linux_raw/mm/types.rs":"dd16b7121b72d3ea75bae6ed67daf3a3f6fe509ae9cc45f1b7d33285049b0e8f","src/backend/linux_raw/mod.rs":"8edbb969111d27b64a90c42307799e0a5cb1828a7bec3cba7cda67ea68b532d7","src/backend/linux_raw/mount/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/mount/syscalls.rs":"30d2a474e867e2453b401eb2e5e4a74b46ffc70efecf6c8cda16a20473a7c066","src/backend/linux_raw/mount/types.rs":"f393fbcfb762043bd4657852b58b9eaa5ace0585af8666c5234fbbd968de55bd","src/backend/linux_raw/net/addr.rs":"2e206bb0b1dec4c8546e15fd1b51b396b9beedc3bf717aa108a5703ed6674d1b","src/backend/linux_raw/net/mod.rs":"5640bbaf4963ac4575b0537173422ed6f6a43f34a8f6b656e7257fbbb13e5bcb","src/backend/linux_raw/net/msghdr.rs":"6ac59d03090cd280664bb20887fd7d818bcfbc7d5beec7d791330ecbfe8bc070","src/backend/linux_raw/net/netdevice.rs":"905992885fa4c813a8bf2dcd5ab6092e760407b7026e484258a628285b5681d9","src/backend/linux_raw/net/read_sockaddr.rs":"ec8e5d4f1389e99884c668ad2530a8e4d2a9fa811690b41ea4ad3cfa9092749b","src/backend/linux_raw/net/send_recv.rs":"e1fe9b647b76c25138d462748e1e594e56ccb72e7dc02bd6c4ad3c94c3f24e64","src/backend/linux_raw/net/sockopt.rs":"ed4ea2b987a76f6474eaf4941a095205aa7d0a428dbe937a074a8f195c4a4453","src/backend/linux_raw/net/syscalls.rs":"7ffe2da0380938b3e40ce5b9607b981d7f44fcf7705e12391e36dcd5257b825a","src/backend/linux_raw/net/write_sockaddr.rs":"e77f69f7535c7c9f8e7fc8fdfc60f208039cd681aa2c97b102edc53092f1eb43","src/backend/linux_raw/param/auxv.rs":"ec3d2fd93c10a36c5e634422d307efda71832ed8f680e53f19cc2882eced5208","src/backend/linux_raw/param/init.rs":"e442eb702dac5c550228196e890d1dd909cd8c2312e58e526d99cd031616466d","src/backend/linux_raw/param/libc_auxv.rs":"b50b675081735f678f73d486cdefd4dc70bf520df1b2d7166bf27b8d17a1d511","src/backend/linux_raw/param/mod.rs":"2e6a1a1c00351b9c88bd615aa923f71d76208df5626dd9bea03067f28f81dc31","src/backend/linux_raw/pid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/pid/syscalls.rs":"d0b5a911940a07bb333ed709e2ecda31965a1aa979689d788af2a063272b0f43","src/backend/linux_raw/pipe/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/pipe/syscalls.rs":"63ea3215bb837d354f30866c7e86a3fd9596bdc3eac6986193694865da99b163","src/backend/linux_raw/pipe/types.rs":"55abd34b363307e6102dfca51fbe9a6dbd42254af1b93e1089711fa19699bd8a","src/backend/linux_raw/prctl/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/prctl/syscalls.rs":"a18b224307e0d27bda9b7b85c75cd6c7ddfe39f5ce52efb7bb0bf0585b757343","src/backend/linux_raw/process/mod.rs":"6b021a8b81a0323b520d89a1566360c0525a58fb0860ce9dcd4b46ef3bcb2fc3","src/backend/linux_raw/process/syscalls.rs":"8e7c6d1c07fbbc1db1ef22f05bcd91ceb2c3e3cbe2c99a158d70139c457d065e","src/backend/linux_raw/process/types.rs":"47ecd87edcd9bcc8f45058610b268c994f3f21a218e9052789c554eabda8c4ba","src/backend/linux_raw/process/wait.rs":"c1c90edeab2179280f86aeca0bd1b7237e1558645c80d98d9febb19b55f4c910","src/backend/linux_raw/pty/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/pty/syscalls.rs":"ae09c4aecc0ae87b1ca58d82efc58007b9dddaae78460d615f48da19d1cd0f89","src/backend/linux_raw/rand/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/rand/syscalls.rs":"8a1f3ba411892718074e48219ac32f0bc017bc68ce99a85398d512c18d0c51d8","src/backend/linux_raw/rand/types.rs":"a21dbb1ab31eed6b59a57520be5a3c603c36d18ea74e67f7527f40835ba209a3","src/backend/linux_raw/reg.rs":"3e321750d91d77c835abd09b06b5851ebf3134d3affb3cce306e38329871bec4","src/backend/linux_raw/runtime/mod.rs":"b2cae8cce3822c3c92942f06ea0b68464040dcac33c6f0f7ee392c6269993347","src/backend/linux_raw/runtime/syscalls.rs":"25679940620395e8e8f67a214b573ecba0d8a01b3d91f909ab36a707f889246a","src/backend/linux_raw/runtime/tls.rs":"364d651b58e0169e22c67586661848d86daff38080a374bb86ce3300794f7e4e","src/backend/linux_raw/shm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/shm/syscalls.rs":"d47734bcaebe004c3b5f1329df75eafd687aaf4ad9d16857dd2e8b2ed7d9e417","src/backend/linux_raw/shm/types.rs":"d6e739cae9a438624d3eb295146f1356e6beb92a12b21904dc5cb3d5dd80c85e","src/backend/linux_raw/system/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/system/syscalls.rs":"94e9f219276dba7f71fe6512a6d2554d3fcf05af08d6376619220b302ddd007d","src/backend/linux_raw/system/types.rs":"fc32d2141cc2305b129bee28ab446a0ce0e0a64505caaef06e3482ea5ae40090","src/backend/linux_raw/termios/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/termios/syscalls.rs":"c1d42cfc87d65a737fb0c38c8e04bd7f25536bbaaf134050b5cd782985a3c4fd","src/backend/linux_raw/termios/types.rs":"37ae0356a50d62dac7c3575f6fea13a4daf8ef10c119673fc9d4d30ac0c36ab6","src/backend/linux_raw/thread/cpu_set.rs":"3a7223d540e20bd45b342bd2781756fa3b9d0d331d3007db91e55a1d65e3a571","src/backend/linux_raw/thread/futex.rs":"2b949694d9c6a2b6fccc6b4cee73673b9cb74903728beb2e88464ea0b3778c3c","src/backend/linux_raw/thread/mod.rs":"93ada3063d5c398901b2ad130797ba1b797ffd814c969780bd5c06cea8e7619c","src/backend/linux_raw/thread/syscalls.rs":"17f9dde80daa01096146f8654fdc3e7ab79f274fb41e885580642da1816d8d4d","src/backend/linux_raw/thread/types.rs":"cc7153e742eacb78acfed2c5977e38a18f21afc9bb6e05bdbd8803c2b7ba1670","src/backend/linux_raw/time/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/linux_raw/time/syscalls.rs":"66e3811ffdd3fd7f9ce679063e6ce80e755149b37ce437045a30d48cf4f67cdd","src/backend/linux_raw/time/types.rs":"430e7798c2332b72d45f9d430e96302211d7195c3b0810d213442bfa5d8295ee","src/backend/linux_raw/ugid/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/linux_raw/ugid/syscalls.rs":"e77637de0015835039562661d3451afeebce0c7d019cf8e93b6572ee4f20f71c","src/backend/linux_raw/vdso.rs":"1ef85f02ba89e5afa5b9b574143ed58c447aed18c4e50eb861183cd0e5cbefb0","src/backend/linux_raw/vdso_wrappers.rs":"cf31e9520c46293e05f1cba001269d9b56db6e3114326ea132e095903a310e9a","src/bitcast.rs":"6b26f35379b3a39b0cf1458d8ac5d5d1bef28e901ef27f0e5248fbfb2d286216","src/buffer.rs":"d5ce44cfd30941fa25d7b46b8039c2e028abc8e9cf20bfbf7dc6ebf3e4b56097","src/check_types.rs":"c1393e8cae99df151e622ccf097b7e9633d2ef5f822464cb692ae4ea3961a065","src/clockid.rs":"d277107e621f60e8daad25fe3aa62cd797499a3b3ab300f8d58bb74ea6c46e47","src/cstr.rs":"5529b770668c0abc904e732e8024fa504d3310da8451687b37298f9fe5581b83","src/event/epoll.rs":"692bfb5224dbc6aecf912a8218c9841d2cc45134dab462738957e69b3ee98251","src/event/eventfd.rs":"81cbd08f7bdf40a6ce1ca692b63da1dc8ba925282990668d9d68f1203e839fa1","src/event/kqueue.rs":"e6022ca584cb0b453a4c4e03be353c97d2f64727278c366758899a0167d48776","src/event/mod.rs":"fe148bcf9de924f3de4e3416b35c8d43a6964c460b12dcd898b2cf2bdc219abf","src/event/pause.rs":"fee4b6daf3ed698e85cd6e2c5b9058ffd84e4d21f6f25b336417a9e1ff200ec4","src/event/poll.rs":"0b3244e1e9c4b2fd17d699ad3ee0af999807836980791e889942b7c710faaa05","src/event/port.rs":"4bfdf5632cf19dac24db34f2be9913c6d34cd7a57168949d7a531b96fc6e9e27","src/event/select.rs":"f0b35f6ba35a4446bcef3b109516a4f1ff6ad295d0d4b53b5a9406aa06079b6d","src/ffi.rs":"88b7b2d797f3c1ed0915aeeebb3f37fc67560eabedcee71ddc39872c12346094","src/fs/abs.rs":"393c537e13cd1436b88fdc8fe124f5cfa4f53d7914c9d291fb265fe2afbede02","src/fs/at.rs":"ebc1b1d64e6e41c793454025293bcd018934f97f72e642cd32a2dda1487bd760","src/fs/constants.rs":"e6cdb9d58bc7959cbdd50a9d7424d715279ca9b8f07192a3af0b36f718a7a1e8","src/fs/copy_file_range.rs":"d3b644374390d482b2ff749a2459458872b57d0dcf9670368739b7833509a7c2","src/fs/dir.rs":"347a52f4ca9ac6321c52e802e97ec90d1b4c62ec955c8996fc17f8f5aed69966","src/fs/fadvise.rs":"33a4eba049ee331913feb8d740320abc58207a69cf02b81bb11291b83110ac5a","src/fs/fcntl.rs":"3cbcfeb62eca4818239a611bd2df35a0781e1a703b30860f15de77063c7e9179","src/fs/fcntl_apple.rs":"e2f23f038083621bcdecc98d02ce1023508afaecdb2ed0fba5c8b70f955301e5","src/fs/fcopyfile.rs":"ce565f61e1fbf2e31086077c2f1d01b6bb3b048915edda87fe9a4a4f5e8ff7e4","src/fs/fd.rs":"cdeddb69610f9bab80cefd74449dce842d7f1c16123958bbd7ea0a3520fdc7bc","src/fs/getpath.rs":"28f6970fc1bbc37bb35c84724b59eac436ea7407a4522e18c2bdacb1fdd2edd9","src/fs/id.rs":"1b5c8a8baf9a9bb1f895f97189cea4e5982a0d35b192afeec6340a6c6222e0cb","src/fs/inotify.rs":"160e05e6f9ce9e966754747d9ac77882228c572860560de95add337cab9caaa2","src/fs/ioctl.rs":"e348aa5f100a51b0181a885ef90e7a8fb558ea35363d09f4fe3989b101054335","src/fs/makedev.rs":"b31f8e58cb1098abd0e7e5e78c7ae00d7760cf94e1a30aafe04fd8f3707c8bbd","src/fs/memfd_create.rs":"b83a5ce6476a226c2098774020481d0dc0ec5c52be09d9a58a877437592e8928","src/fs/mod.rs":"de4b931a3857eef723245a64c123cf79c4d2beccfe413acc6f398c868ef5591e","src/fs/openat2.rs":"8e2b73aa55bb5a6fa5c74fd022133ec5577a1db2dc3ab0f2b022b44cd08cbf4e","src/fs/raw_dir.rs":"88b387dc5701f35d3ce32b0a2d36c8257bfaff1eb5336e01224df88749a93d24","src/fs/seek_from.rs":"f3f5bd18d27808f6be9ab2df4d05515b2b5703e5f1f02e6d36f20507e20fe5cd","src/fs/sendfile.rs":"b0fe446ad86ce44d4fc4b736d819478d71947a4ac27982864174ea16e990a839","src/fs/special.rs":"4fb09337169a48a2f1032487885f68acddf10d130ada2117e3027eb0846e1c12","src/fs/statx.rs":"49023d382193d3041c330b5ef54e6cefe8c1f90280134d141a6f7d4274c43ea4","src/fs/sync.rs":"84f24de0ea53f77bd5c471e097abcbecd19c4843b69607f9be9030eb7c9d5aa5","src/fs/xattr.rs":"a1eacfad1cd089fc1786367211870326e4077b452bf4d6c9b7d3e2e959d484de","src/io/close.rs":"e6f184a806e7671d03c61f09c24a721d410a177c4ac1e08f5039e21fa9caaf5c","src/io/dup.rs":"08926bbe2b2c5dbc3d0ff79bfac8be725b90106c91087c8db9df3928ad05c7c1","src/io/errno.rs":"d014b6a4a4d92cda1e75873a074226101b58bccb4b787985ab00517a3dd9b926","src/io/fcntl.rs":"99fa698c6a6c68ecece7e7d14e0a65e1b6e17d360f028e14f8800d42a355b22c","src/io/ioctl.rs":"56d0fb4f4ab72ca4aca6553d79263be19dedc46a575e61241614404af910fd9e","src/io/mod.rs":"8656db3ce0fc51128d9bc6daac4591a5fedd269333627ebf86d6f045305f9e0b","src/io/read_write.rs":"84930810319605109d5d0d592f1470213ab7d009e1f30b49ee4e2de71afe8ba9","src/io_uring/bindgen_types.rs":"0f38cb903c962abe2fca743e137d191644428582c5e48ca6004f4e3b5bd11631","src/io_uring/mod.rs":"cb2075be0e00929715d288e610a61f4f73f0f9f914c948b4504208e02cd16a67","src/ioctl/bsd.rs":"614e647caad5d7f81b2d570ca0dc77cd942f53a2544e4d1252155370e6fe364a","src/ioctl/linux.rs":"8b78fd0d1b2c078a25a49b7e85d8b2c03c5258c57662b3e3bdd8b7999a685a61","src/ioctl/mod.rs":"430acf575ad2eb49ee300efd1630b55cca1ef7197be7a0c0d4f2fd7feb80f791","src/ioctl/patterns.rs":"8bc7f5020c68ef67667b9078a867eb9bc5ad6ab7c674c577541d360052a299d3","src/kernel_sigset.rs":"f457d0e4b6b10c458a84b4877599f9f4607e6b4693dbc37b80f64a269711f686","src/lib.rs":"7c1bae7844fd63bfdab112e8b3bfa443f7bc7e6b78e59c33b0a162509c855d65","src/maybe_polyfill/no_std/io/mod.rs":"77889bb5c5a4f2e50e38379cdaa5d0fef4b0cafc3da056735df01f6deae75747","src/maybe_polyfill/no_std/mod.rs":"2a10587d0753bd4f849056643fefc98cea90dce266396f8a7bb3a3a1f318e0bb","src/maybe_polyfill/no_std/net/mod.rs":"1e94d21d455bb1da18a4fccb24b3d93ad42698e22dd888b78115d6266c80288a","src/maybe_polyfill/no_std/os/fd/mod.rs":"27ef0afbcb0695cbb15101070f417eb51e0ef85ae66ec967d95e80771d507c47","src/maybe_polyfill/no_std/os/fd/owned.rs":"7b426aa3efc8bf49f153595342209b4f0976464dc2bec9983e2387ebbf7f528e","src/maybe_polyfill/no_std/os/fd/raw.rs":"f3648c7bd4a6ff94bd823ed9e0d99d398e02f24875cf9b25962736999e7c6943","src/maybe_polyfill/no_std/os/mod.rs":"27dab639a765827644005d5f2fcc7c825310606b889cc8dd83f54c9528350dc0","src/maybe_polyfill/no_std/os/windows/io/mod.rs":"5bbcc05c83fee5026dd744a994e0458469466d5be39081baa62df07753b92fd2","src/maybe_polyfill/no_std/os/windows/io/raw.rs":"0a82257f263f02a685536538b656f152041ab406155feab294e93e00969b834f","src/maybe_polyfill/no_std/os/windows/io/socket.rs":"6e3cff673c993ed92f2c7bf980e8a45e0fddf8da3c66248d5853386256a554a5","src/maybe_polyfill/no_std/os/windows/mod.rs":"fdb416f8f231a4e778b5f985b9ae712ece5e1a1402963ad1a5f6a8b9843795f4","src/maybe_polyfill/std/mod.rs":"d0261b88ece984c9947896a89d7f3ff3470603e5bb5681169ebdb56cbd98a9a6","src/mm/madvise.rs":"092ff2064b16960369a908aeed70ec38a16fa2a0207f814cdea379e2f2286954","src/mm/mmap.rs":"7a19699f3b30639f8796cd762b836e74e9aa0950960bb818b1eabdb56fce40f7","src/mm/mod.rs":"b3a6cb838986d45825b912355cedead761211a494ca6f89b2367a2d2157e340e","src/mm/msync.rs":"0cfb895703b8b7bac47426c8f03499f2aced3f4e1f31008243a1a629e9d44a1d","src/mm/userfaultfd.rs":"4d72aee1ba5b6f079064370dcb4fb3a90b359971c869ab8d9727c359f61b971a","src/mount/fsopen.rs":"4a481a5fc1f6eea237a895de76a87edf97e29fc2f3a847fdaaa8d5254acbc71f","src/mount/mod.rs":"3896d1f0059a3c26314af70df27e3a9445c0f21843cd33589850e6f0e186f773","src/mount/mount_unmount.rs":"b888da0c4b59a4552d9058067a357aac9d6f8ccf3c5b2b8d735fb9590b1e9cf0","src/mount/types.rs":"601ae3e10b7dc496fed7f3b40a80e81c6edd7bf13189d7be45c3212d4c684c39","src/msan.rs":"da3e2f18b8e58dc9e4e32cb4bb81dd69bc7026b46d946d46ccb7623a5f89e076","src/net/addr.rs":"43f3cbd731248450d794aff53c8959fff5a4dd38f7598737d4205c614b492405","src/net/mod.rs":"148286bb768ac38e74716bc8aa3fbdc446bb73b2e525524df7b31ed5bb5d1c9b","src/net/netdevice.rs":"1b66af69cedab4be6eac7e8fca0406f880ba5b24d4031b42a473d0b8a2cc7fd2","src/net/send_recv/mod.rs":"8271eb7b5dd292ed46324d8e188e990f47bbdb20a35ca48905c0cf3f7fd3a899","src/net/send_recv/msg.rs":"d44b02ee8275f12c32ee67508aaf88d4d34954ebee037bfe7168527c98ebd0bb","src/net/socket.rs":"89e97e4675396cd09954729370f02e74ebb1433800e07ac3702cf0bf39a5b661","src/net/socket_addr_any.rs":"2723feb96ecffeaa552c21adc63eec887549a28b9748088995e68a053f29b59e","src/net/socketpair.rs":"aa9013de19945bfea3f7d2889cc23abd384717b79f38cd5d045dec09c7517658","src/net/sockopt.rs":"fb17ed2ccf69e62c17f2b7ba5b4f292a443ea34d9f27b4aa5469fabae4062a7f","src/net/types.rs":"7b6611577cc5a850fc7e20d2fac1db9c02264d4899ee932b607b8f381fecc090","src/net/wsa.rs":"29856e6d0108be6b719f6ef973c5396e48ddd58ecd0c2db9ca3e2e0ba4eccb5b","src/not_implemented.rs":"bf526ecc1872186d5fb57dc186c7d156895cd447ae836b44326c0ba475b6bbee","src/param/auxv.rs":"a0239a69a7b2484674f8f556212b45a0f4d3bafbccc23f4cd8bc9ae1fb2883e1","src/param/init.rs":"f44c8dcf26c3d5f8d8b5aa0a7ee5d6e953ad4baeaca88c6f20cb88eaf0f2e3c9","src/param/mod.rs":"c514216b96d9520c9ffc5662c97b24f1f36f56fe1f5f3c9f64175c8b35577288","src/path/arg.rs":"bafbc54acc499e0d0602eb4c15bc4f781f402443742f4346355c8a160df6fc2d","src/path/dec_int.rs":"9a8d20552acb530d69593f8bee5a6dca2c96a9677e04c7925ad7dd2e23cedf08","src/path/mod.rs":"8f4e2cb48cbdbf6844f25232e5a19fcfe22048b5eb2134a1367349a44f88e8ea","src/pid.rs":"31b5cb3f0bb0674a07beb67026a23520d061217b034f40ccaa53c06ddff58336","src/pipe.rs":"e91710663bff11baa2c731637dadd0e608f4577c0a3464028e32800125978176","src/prctl.rs":"86c893026784a698532323a8f05acdab9452b686f62d98de0a7507988dfdf96a","src/process/chdir.rs":"d5ed5d97e1c7a10c5e935f24774d42fbe4f0253620709e86f9e8bff1c8a93b66","src/process/chroot.rs":"01191eeb86295975874019dc675cb0f8f0ca5f79dd78dc9d1c791a952adb3512","src/process/exit.rs":"e8c22fd82e85652eb3ff892ee421d43d8edfa82729815417bd8e1624f254c9c1","src/process/fcntl_getlk.rs":"7fa3ddc36cee2547d49a107dc313ac438b5fde7ba6e9bcb741c549ac151b4364","src/process/id.rs":"71d82dcce9d3cad4bf4cbfa343be688146829e8d111934e16b5766e066ead36a","src/process/ioctl.rs":"93ed942bc6da8e8213a2e2531dc7c99f26fd9f0b805494afc9c5f300cac5acc3","src/process/kill.rs":"7d6d2b15f00c2d0dd08042a1a4498a5080a3a159183ea630cd265283040a6f90","src/process/mod.rs":"5052584844d2f8be30a4389af89be2937065303e7859093bd08cf70152e445b1","src/process/pidfd.rs":"ce9c52d7f8a3d79a285f070a2a8c0536a84a7e31a8dcb0d76def1078ed6f0726","src/process/pidfd_getfd.rs":"88bfa535b25d8983c5d0fe0d5328da31252da7218d96602260739eeb5689529d","src/process/pivot_root.rs":"aeb2464464efd81ca0b940e3e5e3ed1b09380f3e6ba3200972e0ba100f78badc","src/process/prctl.rs":"493da650e869e098c34390c2e9b9be8dfd7cfdfdf0139bbdae1f86639c5d9e92","src/process/priority.rs":"f11cf35ed9eee03a1f84120754e76db51cf64e6a0d609c9c7fc4b373b9eae5fe","src/process/procctl.rs":"70e4b4a7de295eeefc5d595b3829bf99e6bec5fbc04eaefa897eefd9cac12d3f","src/process/rlimit.rs":"10becde11d8b5d3b50aeb21a8fcdf53ec7803bdbbf8d1f4233b8e6c0b85fafb1","src/process/types.rs":"6609099ccb3c582562d3a0c7ef74dd914a794d4c3e88a7d698275828f4e3ac19","src/process/umask.rs":"15841f5e178dd032f8b8def8545c9e98b205e6b1ba70bf9c07c2c0327a8999c0","src/process/wait.rs":"6e9763df5e56390c55aa8028851f20391b104e93016fa0acacaf6063063576f6","src/pty.rs":"8ccdcd92b4c7a452da1068b44b196836f694fa46fa22c446a7f9e1d1dc4172c7","src/rand/getrandom.rs":"50725a6edf19952a33771c8dd671d15feeb2798362291fbc3ca9758110b36e67","src/rand/mod.rs":"cab59332aadd9b679f5b22cbb222d48ee028af5eb9fd4a4d43922da659b895d7","src/runtime_448b8ad740e2a26f.rs":"6c56e2316ca18723314b9d3d2a6a9460bce69b144a245568baa81c061ea04900","src/shm.rs":"8c80096f1a619c43ee078d947129d0dc0ba7c241496335c7a12c91fbc4ddf1c7","src/signal.rs":"666c0678a2da8bb309fade625016982c7dbe4313da9d74dfed99eac5d8c50a7c","src/stdio.rs":"38fff53c9845a0e44742b2c4d1d13632b88f7e53ae8888405a645fdf758c05f6","src/system.rs":"5755d99ea2062ce743ab92d2065e5d278ec82aa72090270c6857cb20b1cd2790","src/termios/ioctl.rs":"8621e7525142d604d7e1bd242132b6fdb5dad048040bcf352acf5213aeb75ef6","src/termios/mod.rs":"1c467add8298266b9a931d76be51a1046e2fd541775e0856f2700c436ef37da6","src/termios/tc.rs":"34a9ba54a0207aad1aa5be7adff79708e91623e3bc5a1a2cac756d66519deafe","src/termios/tty.rs":"25438c7e49621fa64b2777bd7b4de6e74bd9bd1aff324f99daf8619f98c8d013","src/termios/types.rs":"a97f1726c54a37bbc385ec84592be7ede593d9379ab24c32f631f058ecc5593b","src/thread/clock.rs":"e90a1437e5f9a0decce312c3fa0259d100ec85e050cf14a497c6c26a3bd7d32e","src/thread/futex.rs":"5a2919d7e9b161d65c09b6f38e702e1a5772ee5f08295e761c75c344707f8a1a","src/thread/id.rs":"00de3389f3291c53ad5cddbecea34d31d8925a6c53c475a7cb5c0c0acac97492","src/thread/libcap.rs":"73f233865e06180aedea39082a5cf36541147d9faad37b7efc3707bf9de67461","src/thread/membarrier.rs":"0c71af8f563d3e2c3552e220dbf66a31d4c35cfea9fd900d60fe002bb230baea","src/thread/mod.rs":"f6ea8b17a9d465c2fb0e015ac37dd41625bcd085022c39b115ffd96e1c235e44","src/thread/prctl.rs":"ebc63d13874b10d93a1c55868e965638192a1184f0d894502ffad2f1623ca549","src/thread/sched.rs":"d1eb4de567dfd1471293b56a275d1003390d7a56a689ed30bbb3c68ebab3fa73","src/thread/sched_yield.rs":"4208a36afd20e36c9551cf62ea5facaccfed2fffac85aaa66240b777b8b28de0","src/thread/setns.rs":"dd7cf5423ab6c362edea4b45b9c93066dd126ecf950c07f4a815fe26cf89a99e","src/time/clock.rs":"7588a33f5279ee683f39ce72a8d0a8dfcc2e25d3dbc29990e6829d7c2df7d99b","src/time/mod.rs":"d76112ee6510e209410025cb0b13623906577793f6c172f64e59ea5a90218801","src/time/timerfd.rs":"69fdedec9b22dbc542dfbc55a92ad59423001311067e1398fbab49f02cb29c8f","src/timespec.rs":"83d4f354dcd71fd890a67aab1abb9a704a838f00c17f92038044e633c2337896","src/ugid.rs":"b848df26d88bc2c45b0f06d7b037283901697a2faebb0774c7068e64d250491e","src/utils.rs":"a1579936fc8cfec91404cc9f86841be61728cf7c9b4bf406eb13fc621154976f","src/weak.rs":"45b1e4ab84e6a1e121f61084ec88fdb9b0d2e870df6d415349c88fabecd5f7f5"},"package":"891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"} \ No newline at end of file diff --git a/rust_backend/vendor/rustix-1.1.5/.cargo_vcs_info.json b/rust_backend/vendor/rustix-1.1.5/.cargo_vcs_info.json new file mode 100644 index 00000000..f521192c --- /dev/null +++ b/rust_backend/vendor/rustix-1.1.5/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "287214b889865d8e1406a0ee71cc409b6f6191c8" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/rust_backend/vendor/rustix-1.1.4/CHANGES.md b/rust_backend/vendor/rustix-1.1.5/CHANGES.md similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/CHANGES.md rename to rust_backend/vendor/rustix-1.1.5/CHANGES.md diff --git a/rust_backend/vendor/rustix-1.1.4/CODE_OF_CONDUCT.md b/rust_backend/vendor/rustix-1.1.5/CODE_OF_CONDUCT.md similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/CODE_OF_CONDUCT.md rename to rust_backend/vendor/rustix-1.1.5/CODE_OF_CONDUCT.md diff --git a/rust_backend/vendor/rustix-1.1.4/CONTRIBUTING.md b/rust_backend/vendor/rustix-1.1.5/CONTRIBUTING.md similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/CONTRIBUTING.md rename to rust_backend/vendor/rustix-1.1.5/CONTRIBUTING.md diff --git a/rust_backend/vendor/rustix-1.1.4/COPYRIGHT b/rust_backend/vendor/rustix-1.1.5/COPYRIGHT similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/COPYRIGHT rename to rust_backend/vendor/rustix-1.1.5/COPYRIGHT diff --git a/rust_backend/vendor/rustix-1.1.4/Cargo.lock b/rust_backend/vendor/rustix-1.1.5/Cargo.lock similarity index 88% rename from rust_backend/vendor/rustix-1.1.4/Cargo.lock rename to rust_backend/vendor/rustix-1.1.5/Cargo.lock index 8a6344f3..8c1ab725 100644 --- a/rust_backend/vendor/rustix-1.1.4/Cargo.lock +++ b/rust_backend/vendor/rustix-1.1.5/Cargo.lock @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bitflags" @@ -64,9 +64,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "cast" @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "equivalent" @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "flate2" @@ -345,9 +345,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", @@ -390,9 +390,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -427,12 +427,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -448,17 +448,18 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.87" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f0862381daaec758576dcc22eb7bbf4d7efd67328553f3b45a412a51a3fb21" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -476,15 +477,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -506,15 +501,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memoffset" @@ -546,9 +541,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" @@ -587,9 +582,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "plotters" @@ -640,24 +635,24 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -684,9 +679,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -707,9 +702,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rustc-std-workspace-alloc" @@ -725,27 +720,27 @@ checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys", "windows-sys", ] [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" dependencies = [ "bitflags 2.9.1", "criterion", "errno", "flate2", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "memoffset", "once_cell", "rustc-std-workspace-alloc", @@ -779,9 +774,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -815,9 +810,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -853,9 +848,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "slab" @@ -865,9 +860,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "static_assertions" @@ -888,14 +883,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.25.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys", ] @@ -939,11 +934,11 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -952,14 +947,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.110" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de241cdc66a9d91bd84f097039eb140cdc6eec47e0cdbaf9d932a1dd6c35866" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -970,9 +965,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.110" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12fdf6649048f2e3de6d7d5ff3ced779cdedee0e0baffd7dff5cdfa3abc8a52" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -980,9 +975,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.110" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e63d1795c565ac3462334c1e396fd46dbf481c40f51f5072c310717bc4fb309" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", @@ -993,9 +988,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.110" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f9cdac23a5ce71f6bf9f8824898a501e511892791ea2a0c6b8568c68b9cb53" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] @@ -1017,7 +1012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -1030,15 +1025,15 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.9.1", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.14.0", "semver", ] [[package]] name = "web-sys" -version = "0.3.87" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c7c5718134e770ee62af3b6b4a84518ec10101aad610c024b64d6ff29bb1ff" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -1099,6 +1094,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -1118,7 +1119,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "prettyplease", "syn", "wasm-metadata", @@ -1149,7 +1150,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.9.1", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -1168,7 +1169,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "semver", "serde", @@ -1180,18 +1181,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", diff --git a/rust_backend/vendor/rustix-1.1.4/Cargo.toml b/rust_backend/vendor/rustix-1.1.5/Cargo.toml similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/Cargo.toml rename to rust_backend/vendor/rustix-1.1.5/Cargo.toml index bcf61f1c..0d16494b 100644 --- a/rust_backend/vendor/rustix-1.1.4/Cargo.toml +++ b/rust_backend/vendor/rustix-1.1.5/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.63" +rust-version = "1.65" name = "rustix" -version = "1.1.4" +version = "1.1.5" authors = [ "Dan Gohman ", "Jakub Konka ", @@ -275,11 +275,9 @@ check-cfg = [ "cfg(lower_upper_exp_for_non_zero)", "cfg(sanitize_memory)", "cfg(netbsdlike)", - "cfg(rustc_attrs)", "cfg(rustc_diagnostics)", "cfg(solarish)", "cfg(staged_api)", - "cfg(static_assertions)", "cfg(thumb_mode)", "cfg(wasi)", "cfg(wasi_ext)", diff --git a/rust_backend/vendor/rustix-1.1.5/Cargo.toml.orig b/rust_backend/vendor/rustix-1.1.5/Cargo.toml.orig new file mode 100644 index 00000000..656b11cc --- /dev/null +++ b/rust_backend/vendor/rustix-1.1.5/Cargo.toml.orig @@ -0,0 +1,276 @@ +[package] +name = "rustix" +version = "1.1.5" +authors = [ + "Dan Gohman ", + "Jakub Konka ", +] +description = "Safe Rust bindings to POSIX/Unix/Linux/Winsock-like syscalls" +documentation = "https://docs.rs/rustix" +license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" +repository = "https://github.com/bytecodealliance/rustix" +edition = "2021" +keywords = ["api", "file", "network", "safe", "syscall"] +categories = ["os::unix-apis", "date-and-time", "filesystem", "network-programming"] +include = ["src", "build.rs", "Cargo.toml", "COPYRIGHT", "LICENSE*", "/*.md", "benches"] +rust-version = "1.65" + +[hints] +# Most users use a fraction of the rustix API surface area, so this reduces compilation times +mostly-unused = true + +[dependencies] +bitflags = { version = "2.4.0", default-features = false } + +# Special dependencies used in rustc-dep-of-std mode. +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +rustc-std-workspace-alloc = { version = "1.0.0", optional = true } # not aliased here but in lib.rs because of name collision with the alloc feature + +# Dependencies for platforms where linux_raw is supported, in addition to libc: +# +# On Linux on selected architectures, the linux_raw backend is supported, in +# addition to the libc backend. The linux_raw backend is used by default. The +# libc backend can be selected via adding `--cfg=rustix_use_libc` to +# `RUSTFLAGS` or enabling the `use-libc` cargo feature. +[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] +linux-raw-sys = { version = "0.12", default-features = false, features = ["auxvec", "general", "errno", "ioctl", "no_std", "elf"] } +libc_errno = { package = "errno", version = "0.3.10", default-features = false, optional = true } +libc = { version = "0.2.182", default-features = false, optional = true } + +# Dependencies for platforms where only libc is supported: +# +# On all other Unix-family platforms, and under Miri, we always use the libc +# backend, so enable its dependencies unconditionally. +[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] +libc_errno = { package = "errno", version = "0.3.10", default-features = false } +libc = { version = "0.2.182", default-features = false } + +# Additional dependencies for Linux and Android with the libc backend: +# +# Some syscalls do not have libc wrappers, such as in `io_uring`. For these, +# the libc backend uses the linux-raw-sys ABI and `libc::syscall`. +[target.'cfg(all(any(target_os = "linux", target_os = "android"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] +linux-raw-sys = { version = "0.12", default-features = false, features = ["general", "ioctl", "no_std"] } + +# For the libc backend on Windows, use the Winsock API in windows-sys. +[target.'cfg(windows)'.dependencies.windows-sys] +version = ">=0.52, <0.62" +features = [ + "Win32_Foundation", + "Win32_Networking_WinSock", +] + +# For the libc backend on Windows, also use the errno crate, which has Windows +# support. +[target.'cfg(windows)'.dependencies.libc_errno] +version = "0.3.10" +package = "errno" +default-features = false + +[dev-dependencies] +tempfile = "3.5.0" +libc = "0.2.171" +libc_errno = { package = "errno", version = "0.3.10", default-features = false } +serial_test = "2.0.0" +memoffset = "0.9.0" +flate2 = "1.0" +static_assertions = "1.1.0" + +# With Rust 1.70 this can be removed in favor of `std::sync::OnceLock`. +[target.'cfg(windows)'.dev-dependencies] +once_cell = "1.20.3" + +[target.'cfg(all(criterion, not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies] +criterion = "0.4" + +# Add Criterion configuration, as described here: +# +[[bench]] +name = "mod" +harness = false + +[package.metadata.docs.rs] +features = ["all-apis"] +targets = [ + "x86_64-unknown-linux-gnu", + "i686-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-freebsd", + "x86_64-unknown-openbsd", + "x86_64-unknown-netbsd", + "x86_64-unknown-dragonfly", + "x86_64-unknown-illumos", + "x86_64-unknown-redox", + "x86_64-unknown-haiku", + "wasm32-unknown-emscripten", + "wasm32-wasip1", +] + +[features] + +# By default, use `std`. +default = ["std"] + +# This enables use of std. Disabling this enables `#![no_std]`, and requires +# Rust 1.77 or newer. +std = ["bitflags/std", "alloc", "libc?/std", "libc_errno?/std"] + +# Enable this to request the libc backend. +use-libc = ["libc_errno", "libc"] + +# Enable `rustix::event::*`. +event = [] + +# Enable `rustix::fs::*`. +fs = [] + +# Enable `rustix::io_uring::*` (on platforms that support it). +io_uring = ["event", "fs", "net", "thread", "linux-raw-sys/io_uring"] + +# Enable `rustix::mount::*`. +mount = [] + +# Enable `rustix::net::*`. +net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether", "linux-raw-sys/xdp"] + +# Enable `rustix::thread::*`. +thread = ["linux-raw-sys/prctl"] + +# Enable `rustix::process::*`. +process = ["linux-raw-sys/prctl"] + +# Enable `rustix::shm::*`. +shm = ["fs"] + +# Enable `rustix::time::*`. +time = [] + +# Enable `rustix::param::*`. +param = [] + +# Enable `rustix::pty::*`. +pty = ["fs"] + +# Enable `rustix::termios::*`. +termios = [] + +# Enable `rustix::mm::*`. +mm = [] + +# Enable `rustix::pipe::*`. +pipe = [] + +# Enable `rustix::rand::*`. +rand = [] + +# Enable `rustix::stdio::*`. +stdio = [] + +# Enable `rustix::system::*`. +system = ["linux-raw-sys/system"] + +# Enable `rustix::runtime_*::*`. ⚠ This API is undocumented and unstable and +# experimental and not intended for general-purpose use. ⚠ +runtime = ["linux-raw-sys/prctl"] + +# Enable all API features. +# +# This is primarily intended for rustix developers. Users are encouraged to +# enable only those features they need. +all-apis = [ + "event", + "fs", + "io_uring", + "mm", + "mount", + "net", + "param", + "pipe", + "process", + "pty", + "rand", + "runtime", + "shm", + "stdio", + "system", + "termios", + "thread", + "time", +] + +# When using the linux_raw backend, should we use `getauxval` for reading aux +# vectors, instead of `PR_GET_AUXV` or "/proc/self/auxv"? +use-libc-auxv = [] + +# Enable "use-explicitly-provided-auxv" mode, with a public +# `rustix::param::init` function that must be called before anything else in +# rustix. This is unstable and experimental and not intended for +# general-purpose use. +use-explicitly-provided-auxv = [] + +# OS compatibility features + +# Specialize for Linux 4.11 or later +linux_4_11 = [] + +# Specialize for Linux 5.1 or later +linux_5_1 = ["linux_4_11"] + +# Specialize for Linux 5.11 or later +linux_5_11 = ["linux_5_1"] + +# Enable all specializations for the latest Linux versions. +linux_latest = ["linux_5_11"] + +# Enable features which depend on the Rust global allocator, such as functions +# that return owned strings or `Vec`s. +alloc = [] + +# This is used in the port of std to rustix. This is experimental and not meant +# for regular use. +rustc-dep-of-std = [ + "core", + "rustc-std-workspace-alloc", + "linux-raw-sys/rustc-dep-of-std", + "bitflags/rustc-dep-of-std", +] + +# Enable `rustix::io::try_close`. The rustix developers do not intend the +# existence of this feature to imply that anyone should use it. +try_close = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(alloc_c_string)', + 'cfg(alloc_ffi)', + 'cfg(apple)', + 'cfg(asm_experimental_arch)', + 'cfg(bsd)', + 'cfg(core_c_str)', + 'cfg(core_ffi_c)', + 'cfg(core_intrinsics)', + 'cfg(criterion)', + 'cfg(document_experimental_runtime_api)', + 'cfg(error_in_core)', + 'cfg(fix_y2038)', + 'cfg(freebsdlike)', + 'cfg(libc)', + 'cfg(linux_kernel)', + 'cfg(linux_like)', + 'cfg(linux_raw)', + 'cfg(linux_raw_dep)', + 'cfg(lower_upper_exp_for_non_zero)', + 'cfg(sanitize_memory)', + 'cfg(netbsdlike)', + 'cfg(rustc_diagnostics)', + 'cfg(solarish)', + 'cfg(staged_api)', + 'cfg(thumb_mode)', + 'cfg(wasi)', + 'cfg(wasi_ext)', + 'cfg(wasip2)', + 'cfg(target_arch, values("xtensa"))', + 'cfg(target_os, values("cygwin"))', +] diff --git a/rust_backend/vendor/rustix-1.1.4/LICENSE-APACHE b/rust_backend/vendor/rustix-1.1.5/LICENSE-APACHE similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/LICENSE-APACHE rename to rust_backend/vendor/rustix-1.1.5/LICENSE-APACHE diff --git a/rust_backend/vendor/rustix-1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception b/rust_backend/vendor/rustix-1.1.5/LICENSE-Apache-2.0_WITH_LLVM-exception similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception rename to rust_backend/vendor/rustix-1.1.5/LICENSE-Apache-2.0_WITH_LLVM-exception diff --git a/rust_backend/vendor/rustix-1.1.4/LICENSE-MIT b/rust_backend/vendor/rustix-1.1.5/LICENSE-MIT similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/LICENSE-MIT rename to rust_backend/vendor/rustix-1.1.5/LICENSE-MIT diff --git a/rust_backend/vendor/rustix-1.1.4/ORG_CODE_OF_CONDUCT.md b/rust_backend/vendor/rustix-1.1.5/ORG_CODE_OF_CONDUCT.md similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/ORG_CODE_OF_CONDUCT.md rename to rust_backend/vendor/rustix-1.1.5/ORG_CODE_OF_CONDUCT.md diff --git a/rust_backend/vendor/rustix-1.1.4/README.md b/rust_backend/vendor/rustix-1.1.5/README.md similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/README.md rename to rust_backend/vendor/rustix-1.1.5/README.md index 3e553b30..c3df9a8c 100644 --- a/rust_backend/vendor/rustix-1.1.4/README.md +++ b/rust_backend/vendor/rustix-1.1.5/README.md @@ -33,7 +33,7 @@ more portable APIs built on this functionality, see the [`cap-std`], [`memfd`], * linux_raw, which uses raw Linux system calls and vDSO calls, and is supported on Linux on x86-64, x86, aarch64, riscv64gc, powerpc64le, - arm (v5 onwards), mipsel, and mips64el, with stable, nightly, and 1.63 Rust. + arm (v5 onwards), mipsel, and mips64el, with stable, nightly, and 1.65 Rust. - By being implemented entirely in Rust, avoiding `libc`, `errno`, and pthread cancellation, and employing some specialized optimizations, most functions compile down to very efficient code, which can often be fully inlined into @@ -155,12 +155,12 @@ safety types rather than `RawFd`, and the flags parameters to functions such as `tcsetattr` are `enum`s rather than bare integers. And, rustix calls its `tcgetattr` function `tcgetattr`, rather than `Termios::from_fd`. -## Minimum Supported Rust Version (MSRV) +## Minimum Supported Rust Version -This crate currently works on the version of [Rust on Debian stable], which is -currently [Rust 1.63]. This policy may change in the future, in minor version -releases, so users using a fixed version of Rust should pin to a specific -version of this crate. +This crate has a Minimum Supported Rust Version (MSRV) of [Rust 1.65]. + +The current policy is that the minimum Rust version required to use this crate +may be increased in minor releases. ## Minimum Linux Version @@ -171,11 +171,10 @@ oldest Linux version supported by: The specifics of this policy may change in the future, but we intend it to always reflect “very old” Linux versions. -[MSRV]: #minimum-supported-rust-version-msrv -[Rust 1.63]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html +[MSRV]: #minimum-supported-rust-version +[Rust 1.65]: https://blog.rust-lang.org/2022/11/03/Rust-1.65.0/ [any current Rust target]: https://doc.rust-lang.org/nightly/rustc/platform-support.html [kernel.org]: https://www.kernel.org/releases.html -[Rust on Debian stable]: https://packages.debian.org/stable/rust/rustc [Windows Sockets 2]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2 [`nix`]: https://crates.io/crates/nix [`unix`]: https://crates.io/crates/unix diff --git a/rust_backend/vendor/rustix-1.1.4/SECURITY.md b/rust_backend/vendor/rustix-1.1.5/SECURITY.md similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/SECURITY.md rename to rust_backend/vendor/rustix-1.1.5/SECURITY.md diff --git a/rust_backend/vendor/rustix-1.1.4/benches/mod.rs b/rust_backend/vendor/rustix-1.1.5/benches/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/benches/mod.rs rename to rust_backend/vendor/rustix-1.1.5/benches/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/build.rs b/rust_backend/vendor/rustix-1.1.5/build.rs similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/build.rs rename to rust_backend/vendor/rustix-1.1.5/build.rs index 1677ece4..0bde9129 100644 --- a/rust_backend/vendor/rustix-1.1.4/build.rs +++ b/rust_backend/vendor/rustix-1.1.5/build.rs @@ -62,10 +62,8 @@ fn main() { // If experimental features are enabled, auto-detect and use available // features. if rustc_dep_of_std { - use_feature("rustc_attrs"); use_feature("core_intrinsics"); } else if rustix_use_experimental_features { - use_feature_or_nothing("rustc_attrs"); use_feature_or_nothing("core_intrinsics"); } @@ -79,11 +77,6 @@ fn main() { use_feature_or_nothing("error_in_core"); } - // Feature needed for testing. - if use_static_assertions() { - use_feature("static_assertions"); - } - // `LowerExp`/`UpperExp` for `NonZeroI32` etc. if has_lower_upper_exp_for_non_zero() { use_feature("lower_upper_exp_for_non_zero"); @@ -195,11 +188,11 @@ fn main() { println!("cargo:rerun-if-env-changed=CARGO_FEATURE_USE_LIBC"); println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RUSTC_DEP_OF_STD"); println!("cargo:rerun-if-env-changed=CARGO_CFG_MIRI"); -} -fn use_static_assertions() -> bool { - // `offset_from` was made const in Rust 1.65. - can_compile("const unsafe fn foo(p: *const u8) -> isize { p.offset_from(p) }") + // Remove the can_compile() probe artifact so OUT_DIR ends up empty. + // The emitted metadata is not byte-reproducible, and a nondeterministic + // OUT_DIR destabilizes the cache keys of some build systems like Bazel. + let _ = std::fs::remove_file(probe_file()); } fn use_thumb_mode() -> bool { @@ -231,6 +224,10 @@ fn has_feature(feature: &str) -> bool { )) } +fn probe_file() -> PathBuf { + PathBuf::from(var("OUT_DIR").unwrap()).join("rustix_test_can_compile") +} + /// Test whether the rustc at `var("RUSTC")` can compile the given code. fn can_compile>(test: T) -> bool { use std::process::Stdio; @@ -254,14 +251,12 @@ fn can_compile>(test: T) -> bool { std::process::Command::new(rustc) }; - let out_dir = var("OUT_DIR").unwrap(); - let out_file = PathBuf::from(out_dir).join("rustix_test_can_compile"); cmd.arg("--crate-type=rlib") // Don't require `main`. .arg("--emit=metadata") // Do as little as possible but still parse. .arg("--target") .arg(target) .arg("-o") - .arg(out_file) + .arg(probe_file()) .stdout(Stdio::null()); // We don't care about the output (only whether it builds or not) // If Cargo wants to set RUSTFLAGS, use that. diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/c.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/c.rs similarity index 96% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/c.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/c.rs index 5eeff953..fb748979 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/c.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/c.rs @@ -474,30 +474,8 @@ mod readwrite_pv64v2 { ))] pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2}; -// Rust's libc crate lacks statx for Non-glibc targets. -#[cfg(feature = "fs")] -#[cfg(all( - linux_like, - linux_raw_dep, - not(any( - target_os = "emscripten", - target_env = "gnu", - all(target_arch = "loongarch64", target_env = "musl") - )) -))] -mod statx_flags { - pub(crate) use linux_raw_sys::general::{ - STATX_ALL, STATX_ATIME, STATX_BASIC_STATS, STATX_BLOCKS, STATX_BTIME, STATX_CTIME, - STATX_DIOALIGN, STATX_GID, STATX_INO, STATX_MNT_ID, STATX_MODE, STATX_MTIME, STATX_NLINK, - STATX_SIZE, STATX_TYPE, STATX_UID, - }; - - pub(crate) use linux_raw_sys::general::{ - STATX_ATTR_APPEND, STATX_ATTR_AUTOMOUNT, STATX_ATTR_COMPRESSED, STATX_ATTR_DAX, - STATX_ATTR_ENCRYPTED, STATX_ATTR_IMMUTABLE, STATX_ATTR_MOUNT_ROOT, STATX_ATTR_NODUMP, - STATX_ATTR_VERITY, - }; -} +// Rust's libc crate > v0.2.189 may also define these constants, +// so we use an explicit import. #[cfg(feature = "fs")] #[cfg(all( linux_like, @@ -509,7 +487,30 @@ mod statx_flags { all(target_arch = "loongarch64", target_env = "musl") )) ))] -pub(crate) use statx_flags::*; +pub(crate) use linux_raw_sys::general::{ + STATX_ALL, STATX_ATIME, STATX_BASIC_STATS, STATX_BLOCKS, STATX_BTIME, STATX_CTIME, + STATX_DIOALIGN, STATX_GID, STATX_INO, STATX_MNT_ID, STATX_MODE, STATX_MTIME, STATX_NLINK, + STATX_SIZE, STATX_TYPE, STATX_UID, +}; + +// Rust's libc crate > v0.2.189 may also define these constants, +// so we use an explicit import. +#[cfg(feature = "fs")] +#[cfg(all( + linux_like, + linux_raw_dep, + not(any( + target_os = "android", + target_os = "emscripten", + target_env = "gnu", + all(target_arch = "loongarch64", target_env = "musl") + )) +))] +pub(crate) use linux_raw_sys::general::{ + STATX_ATTR_APPEND, STATX_ATTR_AUTOMOUNT, STATX_ATTR_COMPRESSED, STATX_ATTR_DAX, + STATX_ATTR_ENCRYPTED, STATX_ATTR_IMMUTABLE, STATX_ATTR_MOUNT_ROOT, STATX_ATTR_NODUMP, + STATX_ATTR_VERITY, +}; #[cfg(feature = "fs")] #[cfg(target_os = "android")] diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/conv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/conv.rs similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/conv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/conv.rs index 1cb1fc93..c333f5cf 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/conv.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/conv.rs @@ -3,7 +3,10 @@ //! for converting between rustix's types and libc types. use super::c; -#[cfg(all(feature = "alloc", not(any(windows, target_os = "espidf"))))] +#[cfg(all( + feature = "alloc", + not(any(windows, target_os = "espidf", target_os = "horizon")) +))] use super::fd::IntoRawFd as _; use super::fd::{AsRawFd as _, BorrowedFd, FromRawFd as _, LibcFd, OwnedFd, RawFd}; #[cfg(not(windows))] @@ -35,7 +38,12 @@ pub(super) fn borrowed_fd(fd: BorrowedFd<'_>) -> LibcFd { #[cfg(all( feature = "alloc", - not(any(windows, target_os = "espidf", target_os = "redox")) + not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox" + )) ))] #[inline] pub(super) fn owned_fd(fd: OwnedFd) -> LibcFd { diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/epoll.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/epoll.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/epoll.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/epoll.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/poll_fd.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/poll_fd.rs similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/poll_fd.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/poll_fd.rs index fdaa6c68..3cb5dd79 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/poll_fd.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/poll_fd.rs @@ -8,13 +8,18 @@ use bitflags::bitflags; use core::fmt; use core::marker::PhantomData; +#[cfg(not(target_os = "horizon"))] +pub(crate) type RawPollFlags = ffi::c_short; +#[cfg(target_os = "horizon")] +pub(crate) type RawPollFlags = ffi::c_int; + bitflags! { /// `POLL*` flags for use with [`poll`]. /// /// [`poll`]: crate::event::poll #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct PollFlags: ffi::c_short { + pub struct PollFlags: RawPollFlags { /// `POLLIN` const IN = c::POLLIN; /// `POLLPRI` diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/syscalls.rs similarity index 97% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/syscalls.rs index 3827a2f9..26863828 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/syscalls.rs @@ -247,11 +247,7 @@ pub(crate) unsafe fn select( let timeout_data; let timeout_ptr = match timeout { Some(timeout) => { - // Convert from `Timespec` to `c::timeval`. - timeout_data = c::timeval { - tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_usec: ((timeout.tv_nsec + 999) / 1000) as _, - }; + timeout_data = timeout.to_timeval()?; &timeout_data } None => null(), @@ -324,11 +320,7 @@ pub(crate) unsafe fn select( let timeout_data; let timeout_ptr = match timeout { Some(timeout) => { - // Convert from `Timespec` to `c::timeval`. - timeout_data = c::timeval { - tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_usec: ((timeout.tv_nsec + 999) / 1000) as _, - }; + timeout_data = timeout.to_timeval()?; &timeout_data } None => null(), diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/windows_syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/windows_syscalls.rs similarity index 85% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/windows_syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/windows_syscalls.rs index 05394f55..e615bdc4 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/event/windows_syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/event/windows_syscalls.rs @@ -54,14 +54,7 @@ pub(crate) fn select( let timeout_data; let timeout_ptr = match timeout { Some(timeout) => { - // Convert from `Timespec` to `TIMEVAL`. - timeout_data = c::TIMEVAL { - tv_sec: timeout - .tv_sec - .try_into() - .map_err(|_| io::Errno::OPNOTSUPP)?, - tv_usec: ((timeout.tv_nsec + 999) / 1000) as _, - }; + timeout_data = timeout.to_timeval()?; &timeout_data } None => null(), diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/dir.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/dir.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/dir.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/dir.rs index 3c4c3896..65742876 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/dir.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/dir.rs @@ -421,8 +421,8 @@ fn check_dirent_layout(dirent: &c::dirent) { // Check that the basic layouts match. #[cfg(test)] { - assert_eq_size!(libc_dirent, c::dirent); - assert_eq_size!(libc_dirent, c::dirent); + static_assertions::assert_eq_size!(libc_dirent, c::dirent); + static_assertions::assert_eq_size!(libc_dirent, c::dirent); } // Check that the field offsets match. diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/inotify.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/inotify.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/inotify.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/inotify.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/makedev.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/makedev.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/makedev.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/makedev.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/mod.rs similarity index 86% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/mod.rs index e5e821ea..dd584c01 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/mod.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/mod.rs @@ -1,4 +1,7 @@ -#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +#[cfg(all( + feature = "alloc", + not(any(target_os = "espidf", target_os = "horizon", target_os = "redox")) +))] pub(crate) mod dir; #[cfg(linux_kernel)] pub mod inotify; diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/syscalls.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/syscalls.rs index 704188c2..a9239164 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/syscalls.rs @@ -49,11 +49,12 @@ use crate::fs::SealFlags; target_os = "wasi", )))] use crate::fs::StatFs; -#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] use crate::fs::Timestamps; #[cfg(not(any( apple, target_os = "espidf", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi" @@ -672,7 +673,7 @@ pub(crate) fn stat(path: &CStr) -> io::Result { )))] unsafe { #[cfg(test)] - assert_eq_size!(Stat, c::stat); + static_assertions::assert_eq_size!(Stat, c::stat); let mut stat = MaybeUninit::::uninit(); ret(c::stat(c_str(path), stat.as_mut_ptr().cast()))?; @@ -718,7 +719,7 @@ pub(crate) fn lstat(path: &CStr) -> io::Result { )))] unsafe { #[cfg(test)] - assert_eq_size!(Stat, c::stat); + static_assertions::assert_eq_size!(Stat, c::stat); let mut stat = MaybeUninit::::uninit(); ret(c::lstat(c_str(path), stat.as_mut_ptr().cast()))?; @@ -760,7 +761,7 @@ pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io:: )))] unsafe { #[cfg(test)] - assert_eq_size!(Stat, c::stat); + static_assertions::assert_eq_size!(Stat, c::stat); let mut stat = MaybeUninit::::uninit(); ret(c::fstatat( @@ -941,6 +942,7 @@ pub(crate) fn utimensat( c::c_int ) -> c::c_int } + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] extern "C" { fn setattrlist( path: *const ffi::c_char, @@ -950,6 +952,7 @@ pub(crate) fn utimensat( options: c::c_ulong, ) -> c::c_int; } + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] const FSOPT_NOFOLLOW: c::c_ulong = 0x0000_0001; // If we have `utimensat`, use it. @@ -962,8 +965,14 @@ pub(crate) fn utimensat( )); } + // Return `NOSYS` on platforms where `utimensat` cannot be emulated + // because `fork` is unavailable. + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + return Err(io::Errno::NOSYS); + // Convert `times`. We only need this in the child, but do it before // calling `fork` because it might fail. + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] let (attrbuf_size, times, attrs) = times_to_attrlist(times)?; // `setattrlistat` was introduced in 10.13 along with `utimensat`, so @@ -971,6 +980,7 @@ pub(crate) fn utimensat( // Emulate it using `fork`, and `fchdir` and [`setattrlist`]. // // [`setattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setattrlist.2.html + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] match c::fork() { -1 => Err(io::Errno::IO), 0 => { @@ -1563,7 +1573,7 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { )))] unsafe { #[cfg(test)] - assert_eq_size!(Stat, c::stat); + static_assertions::assert_eq_size!(Stat, c::stat); let mut stat = MaybeUninit::::uninit(); ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr().cast()))?; @@ -2742,12 +2752,12 @@ mod tests { #[test] fn test_sizes() { #[cfg(linux_kernel)] - assert_eq_size!(c::loff_t, u64); + static_assertions::assert_eq_size!(c::loff_t, u64); // Assert that `Timestamps` has the expected layout. If we're not fixing // y2038, libc's type should match ours. If we are, it's smaller. #[cfg(not(fix_y2038))] - assert_eq_size!([c::timespec; 2], Timestamps); + static_assertions::assert_eq_size!([c::timespec; 2], Timestamps); #[cfg(fix_y2038)] assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::()); } diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/types.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/types.rs index b8944ad7..6a4ed6d7 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/fs/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/fs/types.rs @@ -1,4 +1,5 @@ use crate::backend::c; +#[cfg(not(target_os = "horizon"))] use crate::ffi; use bitflags::bitflags; @@ -361,6 +362,14 @@ bitflags! { #[cfg(target_os = "freebsd")] const EMPTY_PATH = bitcast!(c::O_EMPTY_PATH); + /// `O_SYMLINK` + #[cfg(any(apple, target_os = "redox"))] + const SYMLINK = bitcast!(c::O_SYMLINK); + + /// `O_NOFOLLOW_ANY` + #[cfg(apple)] + const NOFOLLOW_ANY = bitcast!(c::O_NOFOLLOW_ANY); + /// `O_LARGEFILE` /// /// Rustix and/or libc will automatically set this flag when @@ -619,6 +628,7 @@ impl FileType { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "nto", target_os = "redox", target_os = "vita" diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/errno.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/errno.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/errno.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/errno.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/syscalls.rs index d91e983c..46f7c585 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/syscalls.rs @@ -8,6 +8,7 @@ use crate::fd::{AsFd as _, BorrowedFd, OwnedFd, RawFd}; #[cfg(not(any( target_os = "aix", target_os = "espidf", + target_os = "horizon", target_os = "nto", target_os = "vita", target_os = "wasi" diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/windows_syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/windows_syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io/windows_syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io/windows_syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io_uring/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io_uring/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io_uring/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io_uring/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/io_uring/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/io_uring/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/io_uring/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/io_uring/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mm/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mm/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/mount/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/mount/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/addr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/addr.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/addr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/addr.rs index 1699ffa9..a9b55559 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/addr.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/addr.rs @@ -289,6 +289,9 @@ impl fmt::Debug for SocketAddrUnix { } #[cfg(linux_kernel)] if let Some(name) = self.abstract_name() { + if let Ok(s) = core::str::from_utf8(name) { + return s.fmt(f); + } return name.fmt(f); } "(unnamed)".fmt(f) diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/ext.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/ext.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/ext.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/ext.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/msghdr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/msghdr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/msghdr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/msghdr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/netdevice.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/netdevice.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/netdevice.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/netdevice.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/read_sockaddr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/read_sockaddr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/read_sockaddr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/read_sockaddr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/send_recv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/send_recv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/send_recv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/send_recv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/sockopt.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/sockopt.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/sockopt.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/sockopt.rs index 0a854146..97104883 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/sockopt.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/sockopt.rs @@ -27,6 +27,7 @@ use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatist target_os = "emscripten", target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "netbsd", target_os = "nto", target_os = "vita", @@ -233,18 +234,19 @@ pub(crate) fn set_socket_timeout( return Err(io::Errno::INVAL); } - // Rust's musl libc bindings deprecated `time_t` while they - // transition to 64-bit `time_t`. What we want here is just - // “whatever type `timeval`'s `tv_sec` is”, so we're ok using - // the deprecated type. + let ts = crate::timespec::Timespec { + tv_sec: timeout + .as_secs() + .try_into() + .unwrap_or(crate::timespec::Secs::MAX), + tv_nsec: timeout.subsec_nanos() as _, + }; + let (sec, usec) = ts.to_sec_usec().unwrap_or((crate::timespec::Secs::MAX, 0)); #[allow(deprecated)] - let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX); - - // `subsec_micros` rounds down, so we use `subsec_nanos` and - // manually round up. + let tv_sec = sec.try_into().unwrap_or(c::time_t::MAX); let mut timeout = c::timeval { tv_sec, - tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + tv_usec: usec as _, }; if timeout.tv_sec == 0 && timeout.tv_usec == 0 { timeout.tv_usec = 1; @@ -652,7 +654,7 @@ pub(crate) fn set_ip_add_membership_with_ifindex( setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn) } -#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[cfg(any(apple, target_os = "freebsd", linux_like, solarish, target_os = "aix"))] #[inline] pub(crate) fn set_ip_add_source_membership( fd: BorrowedFd<'_>, @@ -664,7 +666,7 @@ pub(crate) fn set_ip_add_source_membership( setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source) } -#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[cfg(any(apple, target_os = "freebsd", linux_like, solarish, target_os = "aix"))] #[inline] pub(crate) fn set_ip_drop_source_membership( fd: BorrowedFd<'_>, @@ -1324,7 +1326,7 @@ fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_ } } -#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[cfg(any(apple, target_os = "freebsd", linux_like, solarish, target_os = "aix"))] #[inline] fn to_imr_source( multiaddr: &Ipv4Addr, diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/write_sockaddr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/write_sockaddr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/net/write_sockaddr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/net/write_sockaddr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/param/auxv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/param/auxv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/param/auxv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/param/auxv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/param/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/param/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/param/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/param/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pid/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pid/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pid/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pid/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pid/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pid/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pid/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pid/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/syscalls.rs index fe524954..42e56de7 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/syscalls.rs @@ -7,6 +7,7 @@ use crate::io; target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "nto", target_os = "wasi" )))] diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/types.rs similarity index 96% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/types.rs index db04a942..38e25516 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pipe/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pipe/types.rs @@ -109,9 +109,9 @@ mod tests { #[cfg(not(any(apple, target_os = "wasi")))] #[test] fn test_types() { - assert_eq_size!(PipeFlags, c::c_int); + static_assertions::assert_eq_size!(PipeFlags, c::c_int); #[cfg(linux_kernel)] - assert_eq_size!(SpliceFlags, c::c_int); + static_assertions::assert_eq_size!(SpliceFlags, c::c_int); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/prctl/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/prctl/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/prctl/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/prctl/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/prctl/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/prctl/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/prctl/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/prctl/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/syscalls.rs index 3c67f2c3..962ed60b 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/syscalls.rs @@ -10,6 +10,7 @@ use crate::backend::conv::ret_discarded_char_ptr; #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi" @@ -48,6 +49,7 @@ use crate::process::Signal; #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", + target_os = "horizon", target_os = "vita", target_os = "wasi" )))] diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/types.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/types.rs index 4771ddaf..6097b46f 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/types.rs @@ -1,6 +1,7 @@ #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi" diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/wait.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/wait.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/process/wait.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/process/wait.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pty/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pty/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pty/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pty/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/pty/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/pty/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/pty/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/pty/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/rand/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/rand/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/shm/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/shm/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/syscalls.rs index 0e8a7b36..d3aa9b51 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/syscalls.rs @@ -14,6 +14,7 @@ use { #[cfg(not(any( target_os = "emscripten", target_os = "espidf", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi" diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/system/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/system/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/termios/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/termios/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/cpu_set.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/cpu_set.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/cpu_set.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/cpu_set.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/futex.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/futex.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/futex.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/futex.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/syscalls.rs index 84356fee..81f60219 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/syscalls.rs @@ -13,6 +13,7 @@ use crate::pid::Pid; target_os = "emscripten", target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "openbsd", target_os = "redox", target_os = "vita", diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/thread/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/thread/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/types.rs similarity index 96% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/types.rs index b6e0bdc4..35173abc 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/time/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/time/types.rs @@ -107,7 +107,7 @@ impl From for LibcItimerspec { pub(crate) fn as_libc_itimerspec_ptr(itimerspec: &Itimerspec) -> *const c::itimerspec { #[cfg(test)] { - assert_eq_size!(Itimerspec, c::itimerspec); + static_assertions::assert_eq_size!(Itimerspec, c::itimerspec); } crate::utils::as_ptr(itimerspec).cast::() } @@ -125,7 +125,7 @@ pub(crate) fn as_libc_itimerspec_mut_ptr( ) -> *mut c::itimerspec { #[cfg(test)] { - assert_eq_size!(Itimerspec, c::itimerspec); + static_assertions::assert_eq_size!(Itimerspec, c::itimerspec); } itimerspec.as_mut_ptr().cast::() } @@ -260,7 +260,7 @@ mod tests { ))] #[test] fn test_types() { - assert_eq_size!(TimerfdFlags, c::c_int); - assert_eq_size!(TimerfdTimerFlags, c::c_int); + static_assertions::assert_eq_size!(TimerfdFlags, c::c_int); + static_assertions::assert_eq_size!(TimerfdTimerFlags, c::c_int); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/ugid/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/ugid/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/ugid/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/ugid/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/ugid/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/ugid/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/ugid/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/ugid/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/libc/winsock_c.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/libc/winsock_c.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/libc/winsock_c.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/libc/winsock_c.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/aarch64.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/aarch64.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/aarch64.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/aarch64.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/arm.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/arm.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/arm.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/arm.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips32r6.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips32r6.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips32r6.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips32r6.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips64.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips64.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips64.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips64.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips64r6.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips64r6.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mips64r6.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mips64r6.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/powerpc.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/powerpc.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/powerpc.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/powerpc.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/powerpc64.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/powerpc64.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/powerpc64.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/powerpc64.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/riscv64.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/riscv64.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/riscv64.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/riscv64.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/s390x.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/s390x.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/s390x.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/s390x.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/thumb.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/thumb.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/thumb.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/thumb.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/x86.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/x86.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/x86.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/x86.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/x86_64.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/x86_64.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/arch/x86_64.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/arch/x86_64.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/c.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/c.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/c.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/c.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/conv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/conv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/conv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/conv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/epoll.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/epoll.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/epoll.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/epoll.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/poll_fd.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/poll_fd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/poll_fd.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/poll_fd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/event/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/event/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/dir.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/dir.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/dir.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/dir.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/inotify.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/inotify.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/inotify.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/inotify.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/makedev.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/makedev.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/makedev.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/makedev.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/syscalls.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/syscalls.rs index 908272f4..297782d8 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/syscalls.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/syscalls.rs @@ -1720,11 +1720,17 @@ mod tests { #[test] fn test_sizes() { - assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64); - assert_eq_align!(linux_raw_sys::general::__kernel_loff_t, u64); + static_assertions::assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64); + static_assertions::assert_eq_align!(linux_raw_sys::general::__kernel_loff_t, u64); // Assert that `Timestamps` has the expected layout. - assert_eq_size!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); - assert_eq_align!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); + static_assertions::assert_eq_size!( + [linux_raw_sys::general::__kernel_timespec; 2], + Timestamps + ); + static_assertions::assert_eq_align!( + [linux_raw_sys::general::__kernel_timespec; 2], + Timestamps + ); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/types.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/types.rs index fc19a3f4..7abd6653 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/fs/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/fs/types.rs @@ -67,9 +67,10 @@ bitflags! { } bitflags! { - /// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`]. + /// `S_I*` constants for use with [`openat`], [`openat2`], [`chmodat`], and [`fchmod`]. /// /// [`openat`]: crate::fs::openat + /// [`openat2`]: crate::fs::openat2 /// [`chmodat`]: crate::fs::chmodat /// [`fchmod`]: crate::fs::fchmod #[repr(transparent)] diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/errno.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/errno.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/errno.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/errno.rs index 4ddf6df5..895a4b66 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/errno.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/errno.rs @@ -8,7 +8,7 @@ //! Linux uses error codes in `-4095..0`; we use rustc attributes to describe //! this restricted range of values. #![allow(unsafe_code)] -#![cfg_attr(not(rustc_attrs), allow(unused_unsafe))] +#![allow(unused_unsafe)] use crate::backend::c; use crate::backend::fd::RawFd; @@ -46,8 +46,8 @@ use linux_raw_sys::errno; #[derive(Eq, PartialEq, Hash, Copy, Clone)] // Linux returns negated error codes, and we leave them in negated form, so // error codes are in `-4095..0`. -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0xf001))] -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xffff))] +//#[rustc_layout_scalar_valid_range_start(0xf001)] +//#[rustc_layout_scalar_valid_range_end(0xffff)] pub struct Errno(u16); impl Errno { diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io_uring/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io_uring/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io_uring/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io_uring/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io_uring/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io_uring/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/io_uring/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/io_uring/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mm/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mm/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/mount/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/mount/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/addr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/addr.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/addr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/addr.rs index 7138b571..501ca29a 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/addr.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/addr.rs @@ -235,6 +235,9 @@ impl fmt::Debug for SocketAddrUnix { return bytes.fmt(f); } if let Some(name) = self.abstract_name() { + if let Ok(s) = core::str::from_utf8(name) { + return s.fmt(f); + } return name.fmt(f); } "(unnamed)".fmt(f) diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/msghdr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/msghdr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/msghdr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/msghdr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/netdevice.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/netdevice.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/netdevice.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/netdevice.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/read_sockaddr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/read_sockaddr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/read_sockaddr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/read_sockaddr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/send_recv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/send_recv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/send_recv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/send_recv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/sockopt.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/sockopt.rs similarity index 97% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/sockopt.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/sockopt.rs index 85c65b08..a8b28192 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/sockopt.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/sockopt.rs @@ -285,11 +285,18 @@ fn duration_to_linux_sock_timeval(timeout: Option) -> io::Result<__ker if timeout == Duration::ZERO { return Err(io::Errno::INVAL); } - // `subsec_micros` rounds down, so we use `subsec_nanos` and - // manually round up. + + let ts = crate::timespec::Timespec { + tv_sec: timeout + .as_secs() + .try_into() + .unwrap_or(crate::timespec::Secs::MAX), + tv_nsec: timeout.subsec_nanos() as _, + }; + let (sec, usec) = ts.to_sec_usec().unwrap_or((crate::timespec::Secs::MAX, 0)); let mut timeout = __kernel_sock_timeval { - tv_sec: timeout.as_secs().try_into().unwrap_or(i64::MAX), - tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + tv_sec: sec.try_into().unwrap_or(i64::MAX), + tv_usec: usec as _, }; if timeout.tv_sec == 0 && timeout.tv_usec == 0 { timeout.tv_usec = 1; @@ -312,11 +319,17 @@ fn duration_to_linux_old_timeval(timeout: Option) -> io::Result<__kern return Err(io::Errno::INVAL); } - // `subsec_micros` rounds down, so we use `subsec_nanos` and - // manually round up. + let ts = crate::timespec::Timespec { + tv_sec: timeout + .as_secs() + .try_into() + .unwrap_or(crate::timespec::Secs::MAX), + tv_nsec: timeout.subsec_nanos() as _, + }; + let (sec, usec) = ts.to_sec_usec().unwrap_or((crate::timespec::Secs::MAX, 0)); let mut timeout = __kernel_old_timeval { - tv_sec: timeout.as_secs().try_into().unwrap_or(c::c_long::MAX), - tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + tv_sec: sec.try_into().unwrap_or(c::c_long::MAX), + tv_usec: usec as _, }; if timeout.tv_sec == 0 && timeout.tv_usec == 0 { timeout.tv_usec = 1; diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/write_sockaddr.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/write_sockaddr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/net/write_sockaddr.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/net/write_sockaddr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/auxv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/auxv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/auxv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/auxv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/init.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/init.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/init.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/init.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/libc_auxv.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/libc_auxv.rs similarity index 82% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/libc_auxv.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/libc_auxv.rs index d77d3a84..9350bc1a 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/libc_auxv.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/libc_auxv.rs @@ -178,21 +178,21 @@ mod tests { #[test] fn test_abi() { - const_assert_eq!(self::_SC_PAGESIZE, ::libc::_SC_PAGESIZE); - const_assert_eq!(self::_SC_CLK_TCK, ::libc::_SC_CLK_TCK); - const_assert_eq!(self::AT_HWCAP, ::libc::AT_HWCAP); - const_assert_eq!(self::AT_HWCAP2, ::libc::AT_HWCAP2); - const_assert_eq!(self::AT_EXECFN, ::libc::AT_EXECFN); - const_assert_eq!(self::AT_SECURE, ::libc::AT_SECURE); - const_assert_eq!(self::AT_SYSINFO_EHDR, ::libc::AT_SYSINFO_EHDR); - const_assert_eq!(self::AT_MINSIGSTKSZ, ::libc::AT_MINSIGSTKSZ); + static_assertions::const_assert_eq!(self::_SC_PAGESIZE, ::libc::_SC_PAGESIZE); + static_assertions::const_assert_eq!(self::_SC_CLK_TCK, ::libc::_SC_CLK_TCK); + static_assertions::const_assert_eq!(self::AT_HWCAP, ::libc::AT_HWCAP); + static_assertions::const_assert_eq!(self::AT_HWCAP2, ::libc::AT_HWCAP2); + static_assertions::const_assert_eq!(self::AT_EXECFN, ::libc::AT_EXECFN); + static_assertions::const_assert_eq!(self::AT_SECURE, ::libc::AT_SECURE); + static_assertions::const_assert_eq!(self::AT_SYSINFO_EHDR, ::libc::AT_SYSINFO_EHDR); + static_assertions::const_assert_eq!(self::AT_MINSIGSTKSZ, ::libc::AT_MINSIGSTKSZ); #[cfg(feature = "runtime")] - const_assert_eq!(self::AT_PHDR, ::libc::AT_PHDR); + static_assertions::const_assert_eq!(self::AT_PHDR, ::libc::AT_PHDR); #[cfg(feature = "runtime")] - const_assert_eq!(self::AT_PHNUM, ::libc::AT_PHNUM); + static_assertions::const_assert_eq!(self::AT_PHNUM, ::libc::AT_PHNUM); #[cfg(feature = "runtime")] - const_assert_eq!(self::AT_ENTRY, ::libc::AT_ENTRY); + static_assertions::const_assert_eq!(self::AT_ENTRY, ::libc::AT_ENTRY); #[cfg(feature = "runtime")] - const_assert_eq!(self::AT_RANDOM, ::libc::AT_RANDOM); + static_assertions::const_assert_eq!(self::AT_RANDOM, ::libc::AT_RANDOM); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/param/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/param/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pid/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pid/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pid/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pid/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pid/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pid/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pid/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pid/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pipe/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pipe/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/prctl/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/prctl/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/prctl/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/prctl/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/prctl/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/prctl/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/prctl/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/prctl/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/wait.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/wait.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/process/wait.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/process/wait.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pty/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pty/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pty/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pty/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pty/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pty/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/pty/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/pty/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/rand/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/rand/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/reg.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/reg.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/reg.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/reg.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/tls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/tls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/runtime/tls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/runtime/tls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/shm/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/shm/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/system/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/system/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/termios/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/termios/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/cpu_set.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/cpu_set.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/cpu_set.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/cpu_set.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/futex.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/futex.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/futex.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/futex.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/thread/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/thread/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/types.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/time/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/time/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/ugid/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/ugid/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/ugid/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/ugid/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/ugid/syscalls.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/ugid/syscalls.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/ugid/syscalls.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/ugid/syscalls.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/vdso.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/vdso.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/vdso.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/vdso.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/vdso_wrappers.rs b/rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/vdso_wrappers.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/backend/linux_raw/vdso_wrappers.rs rename to rust_backend/vendor/rustix-1.1.5/src/backend/linux_raw/vdso_wrappers.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/bitcast.rs b/rust_backend/vendor/rustix-1.1.5/src/bitcast.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/bitcast.rs rename to rust_backend/vendor/rustix-1.1.5/src/bitcast.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/buffer.rs b/rust_backend/vendor/rustix-1.1.5/src/buffer.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/buffer.rs rename to rust_backend/vendor/rustix-1.1.5/src/buffer.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/check_types.rs b/rust_backend/vendor/rustix-1.1.5/src/check_types.rs similarity index 90% rename from rust_backend/vendor/rustix-1.1.4/src/check_types.rs rename to rust_backend/vendor/rustix-1.1.5/src/check_types.rs index fb7e7a28..9bfb1d74 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/check_types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/check_types.rs @@ -5,8 +5,8 @@ /// Check that the size and alignment of a type match the `sys` bindings. macro_rules! check_type { ($struct:ident) => { - assert_eq_size!($struct, c::$struct); - assert_eq_align!($struct, c::$struct); + ::static_assertions::assert_eq_size!($struct, c::$struct); + ::static_assertions::assert_eq_align!($struct, c::$struct); }; } @@ -14,8 +14,8 @@ macro_rules! check_type { /// renamed to avoid having types like `bindgen_ty_1` in the API. macro_rules! check_renamed_type { ($to:ident, $from:ident) => { - assert_eq_size!($to, c::$from); - assert_eq_align!($to, c::$from); + ::static_assertions::assert_eq_size!($to, c::$from); + ::static_assertions::assert_eq_align!($to, c::$from); }; } @@ -23,7 +23,7 @@ macro_rules! check_renamed_type { /// field in the `sys` bindings. macro_rules! check_struct_field { ($struct:ident, $field:ident) => { - const_assert_eq!( + ::static_assertions::const_assert_eq!( memoffset::offset_of!($struct, $field), memoffset::offset_of!(c::$struct, $field) ); @@ -41,7 +41,7 @@ macro_rules! check_struct_field { /// we've renamed to avoid having types like `bindgen_ty_1` in the API. macro_rules! check_struct_renamed_field { ($struct:ident, $to:ident, $from:ident) => { - const_assert_eq!( + ::static_assertions::const_assert_eq!( memoffset::offset_of!($struct, $to), memoffset::offset_of!(c::$struct, $from) ); @@ -58,7 +58,7 @@ macro_rules! check_struct_renamed_field { /// but the field is not. macro_rules! check_renamed_struct_field { ($to_struct:ident, $from_struct:ident, $field:ident) => { - const_assert_eq!( + ::static_assertions::const_assert_eq!( memoffset::offset_of!($to_struct, $field), memoffset::offset_of!(c::$from_struct, $field) ); @@ -75,7 +75,7 @@ macro_rules! check_renamed_struct_field { /// a field are renamed. macro_rules! check_renamed_struct_renamed_field { ($to_struct:ident, $from_struct:ident, $to:ident, $from:ident) => { - const_assert_eq!( + ::static_assertions::const_assert_eq!( memoffset::offset_of!($to_struct, $to), memoffset::offset_of!(c::$from_struct, $from) ); diff --git a/rust_backend/vendor/rustix-1.1.4/src/clockid.rs b/rust_backend/vendor/rustix-1.1.5/src/clockid.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/clockid.rs rename to rust_backend/vendor/rustix-1.1.5/src/clockid.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/cstr.rs b/rust_backend/vendor/rustix-1.1.5/src/cstr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/cstr.rs rename to rust_backend/vendor/rustix-1.1.5/src/cstr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/epoll.rs b/rust_backend/vendor/rustix-1.1.5/src/event/epoll.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/epoll.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/epoll.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/eventfd.rs b/rust_backend/vendor/rustix-1.1.5/src/event/eventfd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/eventfd.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/eventfd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/kqueue.rs b/rust_backend/vendor/rustix-1.1.5/src/event/kqueue.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/kqueue.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/kqueue.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/event/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/pause.rs b/rust_backend/vendor/rustix-1.1.5/src/event/pause.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/pause.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/pause.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/poll.rs b/rust_backend/vendor/rustix-1.1.5/src/event/poll.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/poll.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/poll.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/port.rs b/rust_backend/vendor/rustix-1.1.5/src/event/port.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/port.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/port.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/event/select.rs b/rust_backend/vendor/rustix-1.1.5/src/event/select.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/event/select.rs rename to rust_backend/vendor/rustix-1.1.5/src/event/select.rs diff --git a/rust_backend/vendor/rustix-1.1.5/src/ffi.rs b/rust_backend/vendor/rustix-1.1.5/src/ffi.rs new file mode 100644 index 00000000..e4d4ee91 --- /dev/null +++ b/rust_backend/vendor/rustix-1.1.5/src/ffi.rs @@ -0,0 +1,16 @@ +//! Utilities related to FFI bindings. +//! +//! This module re-exports a small set of types for backwards compatibility. +//! New code should prefer importing these items directly from +//! `core::ffi`, `alloc::ffi`, or `std::ffi` as appropriate. + +#[cfg(not(windows))] +#[cfg(any(feature = "alloc", feature = "std"))] +pub use alloc::ffi::{CString, NulError}; + +pub use core::ffi::{ + c_char, c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, +}; + +#[cfg(not(windows))] +pub use core::ffi::{CStr, FromBytesWithNulError}; diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/abs.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/abs.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/abs.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/abs.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/at.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/at.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/fs/at.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/at.rs index f35ce5ac..f74bd435 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/fs/at.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/fs/at.rs @@ -20,7 +20,13 @@ use crate::fs::CloneFlags; use crate::fs::RenameFlags; #[cfg(not(target_os = "espidf"))] use crate::fs::Stat; -#[cfg(not(any(apple, target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[cfg(not(any( + apple, + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "wasi" +)))] use crate::fs::{Dev, FileType}; #[cfg(not(any(target_os = "espidf", target_os = "wasi")))] use crate::fs::{Gid, Uid}; @@ -34,7 +40,7 @@ use { alloc::vec::Vec, backend::fd::BorrowedFd, }; -#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] use {crate::fs::Timestamps, crate::timespec::Nsecs}; /// `UTIME_NOW` for use with [`utimensat`]. diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/constants.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/constants.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/fs/constants.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/constants.rs index a4f8e6e0..097435ea 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/fs/constants.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/fs/constants.rs @@ -57,7 +57,7 @@ mod tests { #[test] fn test_layouts() { #[cfg(linux_raw_dep)] - assert_eq_size!(FsWord, linux_raw_sys::general::__fsword_t); + static_assertions::assert_eq_size!(FsWord, linux_raw_sys::general::__fsword_t); // Don't test against `__kernel_mode_t` on platforms where it's a // `u16`. @@ -68,7 +68,7 @@ mod tests { target_arch = "avr", target_arch = "arm", )))] - assert_eq_size!(RawMode, linux_raw_sys::general::__kernel_mode_t); + static_assertions::assert_eq_size!(RawMode, linux_raw_sys::general::__kernel_mode_t); #[cfg(linux_raw_dep)] #[cfg(any( @@ -77,7 +77,7 @@ mod tests { target_arch = "avr", target_arch = "arm", ))] - assert_eq_size!(u16, linux_raw_sys::general::__kernel_mode_t); + static_assertions::assert_eq_size!(u16, linux_raw_sys::general::__kernel_mode_t); let some_stat: Stat = unsafe { core::mem::zeroed() }; diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/copy_file_range.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/copy_file_range.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/copy_file_range.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/copy_file_range.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/dir.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/dir.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/dir.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/dir.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/fadvise.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/fadvise.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/fadvise.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/fadvise.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/fcntl.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/fcntl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/fcntl.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/fcntl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/fcntl_apple.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/fcntl_apple.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/fcntl_apple.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/fcntl_apple.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/fcopyfile.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/fcopyfile.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/fcopyfile.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/fcopyfile.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/fd.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/fd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/fd.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/fd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/getpath.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/getpath.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/getpath.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/getpath.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/id.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/id.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/id.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/id.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/inotify.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/inotify.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/inotify.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/inotify.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/ioctl.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/ioctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/ioctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/ioctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/makedev.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/makedev.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/makedev.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/makedev.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/memfd_create.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/memfd_create.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/memfd_create.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/memfd_create.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/mod.rs similarity index 92% rename from rust_backend/vendor/rustix-1.1.4/src/fs/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/mod.rs index 5dbd7cbb..dfeed87a 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/fs/mod.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/fs/mod.rs @@ -5,7 +5,10 @@ mod at; mod constants; #[cfg(linux_kernel)] mod copy_file_range; -#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +#[cfg(all( + feature = "alloc", + not(any(target_os = "espidf", target_os = "horizon", target_os = "redox")) +))] mod dir; #[cfg(not(any( apple, @@ -70,7 +73,10 @@ pub use at::*; pub use constants::*; #[cfg(linux_kernel)] pub use copy_file_range::copy_file_range; -#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +#[cfg(all( + feature = "alloc", + not(any(target_os = "espidf", target_os = "horizon", target_os = "redox")) +))] pub use dir::{Dir, DirEntry}; #[cfg(not(any( apple, diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/openat2.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/openat2.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/openat2.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/openat2.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/raw_dir.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/raw_dir.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/raw_dir.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/raw_dir.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/seek_from.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/seek_from.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/seek_from.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/seek_from.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/sendfile.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/sendfile.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/sendfile.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/sendfile.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/special.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/special.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/special.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/special.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/statx.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/statx.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/statx.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/statx.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/sync.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/sync.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/sync.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/sync.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/fs/xattr.rs b/rust_backend/vendor/rustix-1.1.5/src/fs/xattr.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/fs/xattr.rs rename to rust_backend/vendor/rustix-1.1.5/src/fs/xattr.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/close.rs b/rust_backend/vendor/rustix-1.1.5/src/io/close.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/close.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/close.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/dup.rs b/rust_backend/vendor/rustix-1.1.5/src/io/dup.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/dup.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/dup.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/errno.rs b/rust_backend/vendor/rustix-1.1.5/src/io/errno.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/errno.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/errno.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/fcntl.rs b/rust_backend/vendor/rustix-1.1.5/src/io/fcntl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/fcntl.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/fcntl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/ioctl.rs b/rust_backend/vendor/rustix-1.1.5/src/io/ioctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/ioctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/ioctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/io/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io/read_write.rs b/rust_backend/vendor/rustix-1.1.5/src/io/read_write.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io/read_write.rs rename to rust_backend/vendor/rustix-1.1.5/src/io/read_write.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io_uring/bindgen_types.rs b/rust_backend/vendor/rustix-1.1.5/src/io_uring/bindgen_types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/io_uring/bindgen_types.rs rename to rust_backend/vendor/rustix-1.1.5/src/io_uring/bindgen_types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/io_uring/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/io_uring/mod.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/io_uring/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/io_uring/mod.rs index f4084ef1..00fde8df 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/io_uring/mod.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/io_uring/mod.rs @@ -1996,8 +1996,8 @@ mod tests { use sys as c; // `io_uring_ptr` is a replacement for `u64`. - assert_eq_size!(io_uring_ptr, u64); - assert_eq_align!(io_uring_ptr, u64); + static_assertions::assert_eq_size!(io_uring_ptr, u64); + static_assertions::assert_eq_align!(io_uring_ptr, u64); // Test that pointers are stored in `io_uring_ptr` in the way that // io_uring stores them in a `u64`. @@ -2014,8 +2014,8 @@ mod tests { } // `io_uring_user_data` is a replacement for `u64`. - assert_eq_size!(io_uring_user_data, u64); - assert_eq_align!(io_uring_user_data, u64); + static_assertions::assert_eq_size!(io_uring_user_data, u64); + static_assertions::assert_eq_align!(io_uring_user_data, u64); // Test that `u64`s and pointers are properly stored in // `io_uring_user_data`. diff --git a/rust_backend/vendor/rustix-1.1.4/src/ioctl/bsd.rs b/rust_backend/vendor/rustix-1.1.5/src/ioctl/bsd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/ioctl/bsd.rs rename to rust_backend/vendor/rustix-1.1.5/src/ioctl/bsd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/ioctl/linux.rs b/rust_backend/vendor/rustix-1.1.5/src/ioctl/linux.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/ioctl/linux.rs rename to rust_backend/vendor/rustix-1.1.5/src/ioctl/linux.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/ioctl/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/ioctl/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/ioctl/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/ioctl/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/ioctl/patterns.rs b/rust_backend/vendor/rustix-1.1.5/src/ioctl/patterns.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/ioctl/patterns.rs rename to rust_backend/vendor/rustix-1.1.5/src/ioctl/patterns.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/kernel_sigset.rs b/rust_backend/vendor/rustix-1.1.5/src/kernel_sigset.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/kernel_sigset.rs rename to rust_backend/vendor/rustix-1.1.5/src/kernel_sigset.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/lib.rs b/rust_backend/vendor/rustix-1.1.5/src/lib.rs similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/src/lib.rs rename to rust_backend/vendor/rustix-1.1.5/src/lib.rs index 8d4a5e52..8109ca2b 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/lib.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/lib.rs @@ -99,7 +99,6 @@ #![deny(missing_docs)] #![allow(stable_features)] #![cfg_attr(linux_raw, deny(unsafe_code))] -#![cfg_attr(rustc_attrs, feature(rustc_attrs))] #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(all(wasi_ext, target_os = "wasi", feature = "std"), feature(wasi_ext))] #![cfg_attr(core_ffi_c, feature(core_ffi_c))] @@ -114,7 +113,15 @@ any(feature = "rustc-dep-of-std", core_intrinsics), feature(core_intrinsics) )] -#![cfg_attr(asm_experimental_arch, feature(asm_experimental_arch))] +#![cfg_attr( + all( + asm_experimental_arch, + not(target_arch = "s390x"), + not(target_arch = "powerpc"), + not(target_arch = "powerpc64") + ), + feature(asm_experimental_arch) +)] #![cfg_attr(not(feature = "all-apis"), allow(dead_code))] // It is common in Linux and libc APIs for types to vary between platforms. #![allow(clippy::unnecessary_cast)] @@ -153,16 +160,6 @@ extern crate rustc_std_workspace_alloc as alloc; #[cfg(all(feature = "alloc", not(feature = "rustc-dep-of-std")))] extern crate alloc; -// Use `static_assertions` macros if we have them, or a polyfill otherwise. -#[cfg(all(test, static_assertions))] -#[macro_use] -#[allow(unused_imports)] -extern crate static_assertions; -#[cfg(all(test, not(static_assertions)))] -#[macro_use] -#[allow(unused_imports)] -mod static_assertions; - pub mod buffer; #[cfg(not(windows))] #[macro_use] @@ -309,12 +306,21 @@ pub mod thread; pub mod time; // "runtime" is also a public API module, but it's only for libc-like users. +// +// People have been observed using it in the wild, so as a counter-measure, +// it now has a name mangled with a random string that will rotate periodically. #[cfg(not(windows))] #[cfg(feature = "runtime")] #[cfg(linux_raw)] #[cfg_attr(not(document_experimental_runtime_api), doc(hidden))] #[cfg_attr(docsrs, doc(cfg(feature = "runtime")))] -pub mod runtime; +pub mod runtime_448b8ad740e2a26f; +#[cfg(not(windows))] +#[cfg(feature = "runtime")] +#[cfg(linux_raw)] +#[cfg_attr(not(document_experimental_runtime_api), doc(hidden))] +#[cfg_attr(docsrs, doc(cfg(feature = "runtime")))] +pub(crate) use runtime_448b8ad740e2a26f as runtime; // Declare "fs" as a non-public module if "fs" isn't enabled but we need it for // reading procfs. diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/io/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/io/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/io/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/io/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/net/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/net/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/net/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/net/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/owned.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/owned.rs similarity index 96% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/owned.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/owned.rs index b6aea61a..ca9b05f3 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/owned.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/owned.rs @@ -37,12 +37,12 @@ use core::mem::forget; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))] +//#[rustc_layout_scalar_valid_range_start(0)] // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a // 32-bit c_int. Below is -2, in two's complement, but that only works out // because c_int is 32 bits. -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr(rustc_attrs, rustc_nonnull_optimization_guaranteed)] +//#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +//#[rustc_nonnull_optimization_guaranteed] #[cfg_attr(staged_api, stable(feature = "io_safety", since = "1.63.0"))] pub struct BorrowedFd<'fd> { fd: RawFd, @@ -61,13 +61,13 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))] +//#[rustc_layout_scalar_valid_range_start(0)] // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a // 32-bit c_int. Below is -2, in two's complement, but that only works out // because c_int is 32 bits. -#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +//#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))] -#[cfg_attr(rustc_attrs, rustc_nonnull_optimization_guaranteed)] +//#[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { fd: RawFd, } diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/raw.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/raw.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/fd/raw.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/fd/raw.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/raw.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/raw.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/raw.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/raw.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/socket.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/socket.rs similarity index 87% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/socket.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/socket.rs index 87692b37..6bb472bc 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/io/socket.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/io/socket.rs @@ -26,17 +26,17 @@ use core::mem::forget; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[cfg_attr(staged_api, rustc_layout_scalar_valid_range_start(0))] +//#[rustc_layout_scalar_valid_range_start(0)] // This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr( - all(staged_api, target_pointer_width = "32"), - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE) -)] -#[cfg_attr( - all(staged_api, target_pointer_width = "64"), - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] -#[cfg_attr(staged_api, rustc_nonnull_optimization_guaranteed)] +//#[cfg_attr( +// target_pointer_width = "32", +// rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE) +//)] +//#[cfg_attr( +// target_pointer_width = "64", +// rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +//)] +//#[rustc_nonnull_optimization_guaranteed] #[cfg_attr(staged_api, stable(feature = "io_safety", since = "1.63.0"))] pub struct BorrowedSocket<'socket> { socket: RawSocket, @@ -52,17 +52,17 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] -#[cfg_attr(staged_api, rustc_layout_scalar_valid_range_start(0))] +//#[rustc_layout_scalar_valid_range_start(0)] // This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr( - all(staged_api, target_pointer_width = "32"), - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE) -)] -#[cfg_attr( - all(staged_api, target_pointer_width = "64"), - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] -#[cfg_attr(staged_api, rustc_nonnull_optimization_guaranteed)] +//#[cfg_attr( +// target_pointer_width = "32", +// rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE) +//)] +//#[cfg_attr( +// target_pointer_width = "64", +// rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +//)] +//#[rustc_nonnull_optimization_guaranteed] #[cfg_attr(staged_api, stable(feature = "io_safety", since = "1.63.0"))] pub struct OwnedSocket { socket: RawSocket, diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/no_std/os/windows/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/no_std/os/windows/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/std/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/std/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/maybe_polyfill/std/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/maybe_polyfill/std/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mm/madvise.rs b/rust_backend/vendor/rustix-1.1.5/src/mm/madvise.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mm/madvise.rs rename to rust_backend/vendor/rustix-1.1.5/src/mm/madvise.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mm/mmap.rs b/rust_backend/vendor/rustix-1.1.5/src/mm/mmap.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mm/mmap.rs rename to rust_backend/vendor/rustix-1.1.5/src/mm/mmap.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mm/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/mm/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mm/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/mm/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mm/msync.rs b/rust_backend/vendor/rustix-1.1.5/src/mm/msync.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mm/msync.rs rename to rust_backend/vendor/rustix-1.1.5/src/mm/msync.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mm/userfaultfd.rs b/rust_backend/vendor/rustix-1.1.5/src/mm/userfaultfd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mm/userfaultfd.rs rename to rust_backend/vendor/rustix-1.1.5/src/mm/userfaultfd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mount/fsopen.rs b/rust_backend/vendor/rustix-1.1.5/src/mount/fsopen.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mount/fsopen.rs rename to rust_backend/vendor/rustix-1.1.5/src/mount/fsopen.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mount/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/mount/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mount/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/mount/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mount/mount_unmount.rs b/rust_backend/vendor/rustix-1.1.5/src/mount/mount_unmount.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mount/mount_unmount.rs rename to rust_backend/vendor/rustix-1.1.5/src/mount/mount_unmount.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/mount/types.rs b/rust_backend/vendor/rustix-1.1.5/src/mount/types.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/mount/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/mount/types.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/msan.rs b/rust_backend/vendor/rustix-1.1.5/src/msan.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/msan.rs rename to rust_backend/vendor/rustix-1.1.5/src/msan.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/addr.rs b/rust_backend/vendor/rustix-1.1.5/src/net/addr.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/net/addr.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/addr.rs index 13e5c24f..0dcc4d1e 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/net/addr.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/net/addr.rs @@ -169,7 +169,7 @@ mod tests { #[test] fn test_layouts() { - assert_eq_size!(SocketAddrLen, c::socklen_t); + static_assertions::assert_eq_size!(SocketAddrLen, c::socklen_t); #[cfg(not(any(windows, target_os = "redox")))] assert_eq!( diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/net/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/netdevice.rs b/rust_backend/vendor/rustix-1.1.5/src/net/netdevice.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/netdevice.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/netdevice.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/send_recv/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/net/send_recv/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/send_recv/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/send_recv/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/send_recv/msg.rs b/rust_backend/vendor/rustix-1.1.5/src/net/send_recv/msg.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/send_recv/msg.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/send_recv/msg.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/socket.rs b/rust_backend/vendor/rustix-1.1.5/src/net/socket.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/socket.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/socket.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/socket_addr_any.rs b/rust_backend/vendor/rustix-1.1.5/src/net/socket_addr_any.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/socket_addr_any.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/socket_addr_any.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/socketpair.rs b/rust_backend/vendor/rustix-1.1.5/src/net/socketpair.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/socketpair.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/socketpair.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/sockopt.rs b/rust_backend/vendor/rustix-1.1.5/src/net/sockopt.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/net/sockopt.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/sockopt.rs index 796a114b..8faef756 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/net/sockopt.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/net/sockopt.rs @@ -156,6 +156,7 @@ use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpStatistics, XdpUmemReg target_os = "emscripten", target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "netbsd", target_os = "nto", target_os = "vita", @@ -1079,7 +1080,7 @@ pub fn set_ip_add_membership_with_ifindex( /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions -#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[cfg(any(apple, target_os = "freebsd", linux_like, solarish, target_os = "aix"))] #[inline] #[doc(alias = "IP_ADD_SOURCE_MEMBERSHIP")] pub fn set_ip_add_source_membership( @@ -1101,7 +1102,7 @@ pub fn set_ip_add_source_membership( /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions -#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[cfg(any(apple, target_os = "freebsd", linux_like, solarish, target_os = "aix"))] #[inline] #[doc(alias = "IP_DROP_SOURCE_MEMBERSHIP")] pub fn set_ip_drop_source_membership( @@ -1814,6 +1815,6 @@ mod tests { // Backend code needs to cast these to `c_int` so make sure that cast // isn't lossy. - assert_eq_size!(Timeout, c_int); + static_assertions::assert_eq_size!(Timeout, c_int); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/types.rs b/rust_backend/vendor/rustix-1.1.5/src/net/types.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/net/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/types.rs index 370f2468..8eca89ca 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/net/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/net/types.rs @@ -740,7 +740,7 @@ impl AddressFamily { #[cfg(target_os = "freebsd")] pub const SLOW: Self = Self(c::AF_SLOW as _); /// `AF_SYS_CONTROL` - #[cfg(apple)] + #[cfg(target_os = "macos")] pub const SYS_CONTROL: Self = Self(c::AF_SYS_CONTROL as _); /// `AF_SYSTEM` #[cfg(apple)] @@ -1116,6 +1116,7 @@ pub mod ipproto { } /// `SYSPROTO_*` constants. +#[cfg(target_os = "macos")] pub mod sysproto { #[cfg(apple)] use { @@ -1124,11 +1125,9 @@ pub mod sysproto { }; /// `SYSPROTO_EVENT` - #[cfg(apple)] pub const EVENT: Protocol = Protocol(new_raw_protocol(c::SYSPROTO_EVENT as _)); /// `SYSPROTO_CONTROL` - #[cfg(apple)] pub const CONTROL: Protocol = Protocol(new_raw_protocol(c::SYSPROTO_CONTROL as _)); } @@ -2114,14 +2113,14 @@ mod tests { // Backend code needs to cast these to `c_int` so make sure that cast isn't // lossy. - assert_eq_size!(RawProtocol, c_int); - assert_eq_size!(Protocol, c_int); - assert_eq_size!(Option, c_int); - assert_eq_size!(Option, c_int); - assert_eq_size!(RawSocketType, c_int); - assert_eq_size!(SocketType, c_int); - assert_eq_size!(SocketFlags, c_int); - assert_eq_size!(SocketAddrStorage, c::sockaddr_storage); + static_assertions::assert_eq_size!(RawProtocol, c_int); + static_assertions::assert_eq_size!(Protocol, c_int); + static_assertions::assert_eq_size!(Option, c_int); + static_assertions::assert_eq_size!(Option, c_int); + static_assertions::assert_eq_size!(RawSocketType, c_int); + static_assertions::assert_eq_size!(SocketType, c_int); + static_assertions::assert_eq_size!(SocketFlags, c_int); + static_assertions::assert_eq_size!(SocketAddrStorage, c::sockaddr_storage); // Rustix doesn't depend on `Option` matching the ABI of a raw // integer for correctness, but it should work nonetheless. @@ -2135,13 +2134,13 @@ mod tests { } #[cfg(linux_kernel)] - assert_eq_size!(UCred, libc::ucred); + static_assertions::assert_eq_size!(UCred, libc::ucred); #[cfg(target_os = "linux")] - assert_eq_size!(super::xdp::XdpUmemReg, c::xdp_umem_reg); + static_assertions::assert_eq_size!(super::xdp::XdpUmemReg, c::xdp_umem_reg); #[cfg(target_os = "linux")] - assert_eq_size!(super::xdp::XdpOptions, c::xdp_options); + static_assertions::assert_eq_size!(super::xdp::XdpOptions, c::xdp_options); #[cfg(target_os = "linux")] - assert_eq_size!(super::xdp::XdpDesc, c::xdp_desc); + static_assertions::assert_eq_size!(super::xdp::XdpDesc, c::xdp_desc); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/net/wsa.rs b/rust_backend/vendor/rustix-1.1.5/src/net/wsa.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/net/wsa.rs rename to rust_backend/vendor/rustix-1.1.5/src/net/wsa.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/not_implemented.rs b/rust_backend/vendor/rustix-1.1.5/src/not_implemented.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/not_implemented.rs rename to rust_backend/vendor/rustix-1.1.5/src/not_implemented.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/param/auxv.rs b/rust_backend/vendor/rustix-1.1.5/src/param/auxv.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/param/auxv.rs rename to rust_backend/vendor/rustix-1.1.5/src/param/auxv.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/param/init.rs b/rust_backend/vendor/rustix-1.1.5/src/param/init.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/param/init.rs rename to rust_backend/vendor/rustix-1.1.5/src/param/init.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/param/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/param/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/param/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/param/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/path/arg.rs b/rust_backend/vendor/rustix-1.1.5/src/path/arg.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/path/arg.rs rename to rust_backend/vendor/rustix-1.1.5/src/path/arg.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/path/dec_int.rs b/rust_backend/vendor/rustix-1.1.5/src/path/dec_int.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/path/dec_int.rs rename to rust_backend/vendor/rustix-1.1.5/src/path/dec_int.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/path/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/path/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/path/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/path/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/pid.rs b/rust_backend/vendor/rustix-1.1.5/src/pid.rs similarity index 94% rename from rust_backend/vendor/rustix-1.1.4/src/pid.rs rename to rust_backend/vendor/rustix-1.1.5/src/pid.rs index fc0dc9e6..d984c28e 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/pid.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/pid.rs @@ -148,16 +148,16 @@ mod tests { fn test_sizes() { use core::mem::transmute; - assert_eq_size!(RawPid, NonZeroI32); - assert_eq_size!(RawPid, Pid); - assert_eq_size!(RawPid, Option); + static_assertions::assert_eq_size!(RawPid, NonZeroI32); + static_assertions::assert_eq_size!(RawPid, Pid); + static_assertions::assert_eq_size!(RawPid, Option); // Rustix doesn't depend on `Option` matching the ABI of a raw integer // for correctness, but it should work nonetheless. - const_assert_eq!(0 as RawPid, unsafe { + static_assertions::const_assert_eq!(0 as RawPid, unsafe { transmute::, RawPid>(None) }); - const_assert_eq!(4567 as RawPid, unsafe { + static_assertions::const_assert_eq!(4567 as RawPid, unsafe { transmute::, RawPid>(Some(Pid::from_raw_unchecked(4567))) }); } diff --git a/rust_backend/vendor/rustix-1.1.4/src/pipe.rs b/rust_backend/vendor/rustix-1.1.5/src/pipe.rs similarity index 99% rename from rust_backend/vendor/rustix-1.1.4/src/pipe.rs rename to rust_backend/vendor/rustix-1.1.5/src/pipe.rs index 267c99d5..ef1c25c6 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/pipe.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/pipe.rs @@ -9,6 +9,7 @@ use crate::{backend, io}; windows, target_os = "espidf", target_os = "haiku", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi", diff --git a/rust_backend/vendor/rustix-1.1.4/src/prctl.rs b/rust_backend/vendor/rustix-1.1.5/src/prctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/prctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/prctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/chdir.rs b/rust_backend/vendor/rustix-1.1.5/src/process/chdir.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/chdir.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/chdir.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/chroot.rs b/rust_backend/vendor/rustix-1.1.5/src/process/chroot.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/chroot.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/chroot.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/exit.rs b/rust_backend/vendor/rustix-1.1.5/src/process/exit.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/exit.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/exit.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/fcntl_getlk.rs b/rust_backend/vendor/rustix-1.1.5/src/process/fcntl_getlk.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/fcntl_getlk.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/fcntl_getlk.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/id.rs b/rust_backend/vendor/rustix-1.1.5/src/process/id.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/id.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/id.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/ioctl.rs b/rust_backend/vendor/rustix-1.1.5/src/process/ioctl.rs similarity index 75% rename from rust_backend/vendor/rustix-1.1.4/src/process/ioctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/ioctl.rs index 5362504f..e0ba0018 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/process/ioctl.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/process/ioctl.rs @@ -22,35 +22,17 @@ use backend::fd::AsFd; /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=tty&sektion=4 /// [NetBSD]: https://man.netbsd.org/tty.4 /// [OpenBSD]: https://man.openbsd.org/tty.4 -#[cfg(not(any( - windows, - target_os = "aix", - target_os = "horizon", - target_os = "redox", - target_os = "wasi" -)))] +#[cfg(not(any(windows, target_os = "aix", target_os = "redox", target_os = "wasi")))] #[inline] #[doc(alias = "TIOCSCTTY")] pub fn ioctl_tiocsctty(fd: Fd) -> io::Result<()> { unsafe { ioctl::ioctl(fd, Tiocsctty) } } -#[cfg(not(any( - windows, - target_os = "aix", - target_os = "horizon", - target_os = "redox", - target_os = "wasi" -)))] +#[cfg(not(any(windows, target_os = "aix", target_os = "redox", target_os = "wasi")))] struct Tiocsctty; -#[cfg(not(any( - windows, - target_os = "aix", - target_os = "horizon", - target_os = "redox", - target_os = "wasi" -)))] +#[cfg(not(any(windows, target_os = "aix", target_os = "redox", target_os = "wasi")))] unsafe impl ioctl::Ioctl for Tiocsctty { type Output = (); diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/kill.rs b/rust_backend/vendor/rustix-1.1.5/src/process/kill.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/kill.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/kill.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/process/mod.rs similarity index 91% rename from rust_backend/vendor/rustix-1.1.4/src/process/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/mod.rs index 7509b060..d5ac684c 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/process/mod.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/process/mod.rs @@ -17,7 +17,12 @@ mod exit; mod fcntl_getlk; #[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. mod id; -#[cfg(not(any(target_os = "aix", target_os = "espidf", target_os = "vita")))] +#[cfg(not(any( + target_os = "aix", + target_os = "horizon", + target_os = "espidf", + target_os = "vita" +)))] mod ioctl; #[cfg(not(any(target_os = "espidf", target_os = "wasi")))] mod kill; @@ -74,7 +79,12 @@ pub use exit::*; pub use fcntl_getlk::*; #[cfg(not(target_os = "wasi"))] pub use id::*; -#[cfg(not(any(target_os = "aix", target_os = "espidf", target_os = "vita")))] +#[cfg(not(any( + target_os = "aix", + target_os = "espidf", + target_os = "horizon", + target_os = "vita" +)))] pub use ioctl::*; #[cfg(not(any(target_os = "espidf", target_os = "wasi")))] pub use kill::*; @@ -103,6 +113,7 @@ pub use rlimit::*; target_os = "emscripten", target_os = "espidf", target_os = "fuchsia", + target_os = "horizon", target_os = "redox", target_os = "vita", target_os = "wasi" diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/pidfd.rs b/rust_backend/vendor/rustix-1.1.5/src/process/pidfd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/pidfd.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/pidfd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/pidfd_getfd.rs b/rust_backend/vendor/rustix-1.1.5/src/process/pidfd_getfd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/pidfd_getfd.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/pidfd_getfd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/pivot_root.rs b/rust_backend/vendor/rustix-1.1.5/src/process/pivot_root.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/pivot_root.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/pivot_root.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/prctl.rs b/rust_backend/vendor/rustix-1.1.5/src/process/prctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/prctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/prctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/priority.rs b/rust_backend/vendor/rustix-1.1.5/src/process/priority.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/priority.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/priority.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/procctl.rs b/rust_backend/vendor/rustix-1.1.5/src/process/procctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/procctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/procctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/rlimit.rs b/rust_backend/vendor/rustix-1.1.5/src/process/rlimit.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/rlimit.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/rlimit.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/types.rs b/rust_backend/vendor/rustix-1.1.5/src/process/types.rs similarity index 96% rename from rust_backend/vendor/rustix-1.1.4/src/process/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/types.rs index 8b324190..f7ca91ef 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/process/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/process/types.rs @@ -4,8 +4,11 @@ #![allow(unsafe_code)] +#[cfg(not(target_os = "horizon"))] use crate::backend::c; +#[cfg(not(target_os = "horizon"))] use crate::pid::Pid; +#[cfg(not(target_os = "horizon"))] use core::mem::transmute; /// File lock data structure used in [`fcntl_getlk`]. diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/umask.rs b/rust_backend/vendor/rustix-1.1.5/src/process/umask.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/umask.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/umask.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/process/wait.rs b/rust_backend/vendor/rustix-1.1.5/src/process/wait.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/process/wait.rs rename to rust_backend/vendor/rustix-1.1.5/src/process/wait.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/pty.rs b/rust_backend/vendor/rustix-1.1.5/src/pty.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/pty.rs rename to rust_backend/vendor/rustix-1.1.5/src/pty.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/rand/getrandom.rs b/rust_backend/vendor/rustix-1.1.5/src/rand/getrandom.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/rand/getrandom.rs rename to rust_backend/vendor/rustix-1.1.5/src/rand/getrandom.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/rand/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/rand/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/rand/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/rand/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/runtime.rs b/rust_backend/vendor/rustix-1.1.5/src/runtime_448b8ad740e2a26f.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/runtime.rs rename to rust_backend/vendor/rustix-1.1.5/src/runtime_448b8ad740e2a26f.rs index 9b612b8b..698f44f2 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/runtime.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/runtime_448b8ad740e2a26f.rs @@ -822,8 +822,8 @@ mod tests { // check_types macros because we want to test compatibility with actual // libc, not the `crate::backend::c` which might be our own // implementation. - assert_eq_size!(Siginfo, libc::siginfo_t); - assert_eq_align!(Siginfo, libc::siginfo_t); + static_assertions::assert_eq_size!(Siginfo, libc::siginfo_t); + static_assertions::assert_eq_align!(Siginfo, libc::siginfo_t); assert_eq!( memoffset::span_of!(Siginfo, ..), memoffset::span_of!(Siginfo, __bindgen_anon_1) @@ -842,8 +842,8 @@ mod tests { ); // c-scape assumes rustix's `Stack` matches libc's. Similar to above. - assert_eq_size!(Stack, libc::stack_t); - assert_eq_align!(Stack, libc::stack_t); + static_assertions::assert_eq_size!(Stack, libc::stack_t); + static_assertions::assert_eq_align!(Stack, libc::stack_t); assert_eq!( memoffset::span_of!(Stack, ss_sp), memoffset::span_of!(libc::stack_t, ss_sp) @@ -900,8 +900,8 @@ mod tests { sa_flags, sa_mask ); - assert_eq_size!(KernelSigactionFlags, crate::ffi::c_ulong); - assert_eq_align!(KernelSigactionFlags, crate::ffi::c_ulong); + static_assertions::assert_eq_size!(KernelSigactionFlags, crate::ffi::c_ulong); + static_assertions::assert_eq_align!(KernelSigactionFlags, crate::ffi::c_ulong); check_renamed_type!(KernelSigrestore, __sigrestore_t); check_renamed_type!(KernelSighandler, __kernel_sighandler_t); diff --git a/rust_backend/vendor/rustix-1.1.4/src/shm.rs b/rust_backend/vendor/rustix-1.1.5/src/shm.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/shm.rs rename to rust_backend/vendor/rustix-1.1.5/src/shm.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/signal.rs b/rust_backend/vendor/rustix-1.1.5/src/signal.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/signal.rs rename to rust_backend/vendor/rustix-1.1.5/src/signal.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/stdio.rs b/rust_backend/vendor/rustix-1.1.5/src/stdio.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/stdio.rs rename to rust_backend/vendor/rustix-1.1.5/src/stdio.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/system.rs b/rust_backend/vendor/rustix-1.1.5/src/system.rs similarity index 98% rename from rust_backend/vendor/rustix-1.1.4/src/system.rs rename to rust_backend/vendor/rustix-1.1.5/src/system.rs index 70204aef..d3cad441 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/system.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/system.rs @@ -6,7 +6,12 @@ use crate::backend; #[cfg(target_os = "linux")] use crate::backend::c; use crate::ffi::CStr; -#[cfg(not(any(target_os = "espidf", target_os = "emscripten", target_os = "vita")))] +#[cfg(not(any( + target_os = "espidf", + target_os = "emscripten", + target_os = "horizon", + target_os = "vita" +)))] use crate::io; use core::fmt; diff --git a/rust_backend/vendor/rustix-1.1.4/src/termios/ioctl.rs b/rust_backend/vendor/rustix-1.1.5/src/termios/ioctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/termios/ioctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/termios/ioctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/termios/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/termios/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/termios/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/termios/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/termios/tc.rs b/rust_backend/vendor/rustix-1.1.5/src/termios/tc.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/termios/tc.rs rename to rust_backend/vendor/rustix-1.1.5/src/termios/tc.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/termios/tty.rs b/rust_backend/vendor/rustix-1.1.5/src/termios/tty.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/termios/tty.rs rename to rust_backend/vendor/rustix-1.1.5/src/termios/tty.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/termios/types.rs b/rust_backend/vendor/rustix-1.1.5/src/termios/types.rs similarity index 97% rename from rust_backend/vendor/rustix-1.1.4/src/termios/types.rs rename to rust_backend/vendor/rustix-1.1.5/src/termios/types.rs index 899fa74b..71d9fb80 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/termios/types.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/termios/types.rs @@ -1453,8 +1453,8 @@ mod tests { check_renamed_type!(OutputModes, tcflag_t); check_renamed_type!(ControlModes, tcflag_t); check_renamed_type!(LocalModes, tcflag_t); - assert_eq_size!(u8, libc::cc_t); - assert_eq_size!(types::tcflag_t, libc::tcflag_t); + static_assertions::assert_eq_size!(u8, libc::cc_t); + static_assertions::assert_eq_size!(types::tcflag_t, libc::tcflag_t); check_renamed_struct!(Winsize, winsize, ws_row, ws_col, ws_xpixel, ws_ypixel); @@ -1482,14 +1482,14 @@ mod tests { // On everything except PowerPC, `termios` matches `termios2` except // for the addition of `c_ispeed` and `c_ospeed`. #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] - const_assert_eq!( + static_assertions::const_assert_eq!( memoffset::offset_of!(Termios, input_speed), core::mem::size_of::() ); // On PowerPC, `termios2` is `termios`. #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] - assert_eq_size!(c::termios2, c::termios); + static_assertions::assert_eq_size!(c::termios2, c::termios); } #[cfg(not(linux_raw))] @@ -1525,7 +1525,9 @@ mod tests { )), not(all(libc, target_os = "android")) )))] - const_assert!(core::mem::size_of::() >= core::mem::size_of::()); + static_assertions::const_assert!( + core::mem::size_of::() >= core::mem::size_of::() + ); check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag); check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag); @@ -1572,8 +1574,8 @@ mod tests { )))] fn termios_legacy() { // Check that our doc aliases above are correct. - const_assert_eq!(c::EXTA, c::B19200); - const_assert_eq!(c::EXTB, c::B38400); + static_assertions::const_assert_eq!(c::EXTA, c::B19200); + static_assertions::const_assert_eq!(c::EXTB, c::B38400); } #[cfg(bsd)] @@ -1581,10 +1583,10 @@ mod tests { fn termios_bsd() { // On BSD platforms we can assume that the `B*` constants have their // arbitrary integer speed value. Confirm this. - const_assert_eq!(c::B0, 0); - const_assert_eq!(c::B50, 50); - const_assert_eq!(c::B19200, 19200); - const_assert_eq!(c::B38400, 38400); + static_assertions::const_assert_eq!(c::B0, 0); + static_assertions::const_assert_eq!(c::B50, 50); + static_assertions::const_assert_eq!(c::B19200, 19200); + static_assertions::const_assert_eq!(c::B38400, 38400); } #[test] @@ -1612,13 +1614,13 @@ mod tests { // When using `termios2`, we assume that we can add the optional actions // value to the ioctl request code. Test this assumption. - const_assert_eq!(c::TCSETS2, c::TCSETS2 + 0); - const_assert_eq!(c::TCSETSW2, c::TCSETS2 + 1); - const_assert_eq!(c::TCSETSF2, c::TCSETS2 + 2); + static_assertions::const_assert_eq!(c::TCSETS2, c::TCSETS2 + 0); + static_assertions::const_assert_eq!(c::TCSETSW2, c::TCSETS2 + 1); + static_assertions::const_assert_eq!(c::TCSETSF2, c::TCSETS2 + 2); - const_assert_eq!(c::TCSANOW - c::TCSANOW, 0); - const_assert_eq!(c::TCSADRAIN - c::TCSANOW, 1); - const_assert_eq!(c::TCSAFLUSH - c::TCSANOW, 2); + static_assertions::const_assert_eq!(c::TCSANOW - c::TCSANOW, 0); + static_assertions::const_assert_eq!(c::TCSADRAIN - c::TCSANOW, 1); + static_assertions::const_assert_eq!(c::TCSAFLUSH - c::TCSANOW, 2); // MIPS is different here. #[cfg(any( @@ -1639,9 +1641,9 @@ mod tests { target_arch = "mips64r6" )))] { - const_assert_eq!(c::TCSANOW, 0); - const_assert_eq!(c::TCSADRAIN, 1); - const_assert_eq!(c::TCSAFLUSH, 2); + static_assertions::const_assert_eq!(c::TCSANOW, 0); + static_assertions::const_assert_eq!(c::TCSADRAIN, 1); + static_assertions::const_assert_eq!(c::TCSAFLUSH, 2); } } @@ -1649,6 +1651,6 @@ mod tests { #[test] fn termios_cibaud() { // Test an assumption. - const_assert_eq!(c::CIBAUD, c::CBAUD << c::IBSHIFT); + static_assertions::const_assert_eq!(c::CIBAUD, c::CBAUD << c::IBSHIFT); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/clock.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/clock.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/clock.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/clock.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/futex.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/futex.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/futex.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/futex.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/id.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/id.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/id.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/id.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/libcap.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/libcap.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/libcap.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/libcap.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/membarrier.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/membarrier.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/membarrier.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/membarrier.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/prctl.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/prctl.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/prctl.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/prctl.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/sched.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/sched.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/sched.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/sched.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/sched_yield.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/sched_yield.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/sched_yield.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/sched_yield.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/thread/setns.rs b/rust_backend/vendor/rustix-1.1.5/src/thread/setns.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/thread/setns.rs rename to rust_backend/vendor/rustix-1.1.5/src/thread/setns.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/time/clock.rs b/rust_backend/vendor/rustix-1.1.5/src/time/clock.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/time/clock.rs rename to rust_backend/vendor/rustix-1.1.5/src/time/clock.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/time/mod.rs b/rust_backend/vendor/rustix-1.1.5/src/time/mod.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/time/mod.rs rename to rust_backend/vendor/rustix-1.1.5/src/time/mod.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/time/timerfd.rs b/rust_backend/vendor/rustix-1.1.5/src/time/timerfd.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/time/timerfd.rs rename to rust_backend/vendor/rustix-1.1.5/src/time/timerfd.rs diff --git a/rust_backend/vendor/rustix-1.1.4/src/timespec.rs b/rust_backend/vendor/rustix-1.1.5/src/timespec.rs similarity index 80% rename from rust_backend/vendor/rustix-1.1.4/src/timespec.rs rename to rust_backend/vendor/rustix-1.1.5/src/timespec.rs index a6458bbc..9d06f203 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/timespec.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/timespec.rs @@ -203,6 +203,41 @@ impl Timespec { }) .and_then(|millis| c::c_int::try_from(millis).ok()) } + + /// Convert `Timespec` to seconds and microseconds, rounding up fractional + /// microseconds and carrying any overflow into seconds. + #[inline] + pub(crate) fn to_sec_usec(&self) -> Option<(Secs, u32)> { + let mut sec = self.tv_sec; + let mut usec = (self.tv_nsec + 999) / 1000; + if usec >= 1_000_000 { + sec = sec.checked_add(1)?; + usec -= 1_000_000; + } + Some((sec, usec as u32)) + } + + /// Convert from `Timespec` to `c::timeval`, rounding up fractional + /// microseconds and carrying any overflow into seconds. + #[cfg(all(any(libc, target_os = "wasi"), not(windows)))] + pub(crate) fn to_timeval(&self) -> crate::io::Result { + let (sec, usec) = self.to_sec_usec().ok_or(crate::io::Errno::INVAL)?; + Ok(c::timeval { + tv_sec: sec.try_into().map_err(|_| crate::io::Errno::INVAL)?, + tv_usec: usec as _, + }) + } + + /// Convert from `Timespec` to `c::TIMEVAL`, rounding up fractional + /// microseconds and carrying any overflow into seconds. + #[cfg(windows)] + pub(crate) fn to_timeval(&self) -> crate::io::Result { + let (sec, usec) = self.to_sec_usec().ok_or(crate::io::Errno::OPNOTSUPP)?; + Ok(c::TIMEVAL { + tv_sec: sec.try_into().map_err(|_| crate::io::Errno::OPNOTSUPP)?, + tv_usec: usec as _, + }) + } } impl TryFrom for Duration { @@ -307,7 +342,7 @@ impl From for LibcTimespec { pub(crate) fn as_libc_timespec_ptr(timespec: &Timespec) -> *const c::timespec { #[cfg(test)] { - assert_eq_size!(Timespec, c::timespec); + static_assertions::assert_eq_size!(Timespec, c::timespec); } crate::utils::as_ptr(timespec).cast::() } @@ -318,7 +353,7 @@ pub(crate) fn as_libc_timespec_mut_ptr( ) -> *mut c::timespec { #[cfg(test)] { - assert_eq_size!(Timespec, c::timespec); + static_assertions::assert_eq_size!(Timespec, c::timespec); } timespec.as_mut_ptr().cast::() } @@ -376,9 +411,11 @@ mod tests { #[test] fn test_sizes() { - assert_eq_size!(Secs, u64); - const_assert!(core::mem::size_of::() >= core::mem::size_of::<(u64, u32)>()); - const_assert!(core::mem::size_of::() >= 4); + static_assertions::assert_eq_size!(Secs, u64); + static_assertions::const_assert!( + core::mem::size_of::() >= core::mem::size_of::<(u64, u32)>() + ); + static_assertions::const_assert!(core::mem::size_of::() >= 4); let mut t = Timespec { tv_sec: 0, @@ -394,12 +431,32 @@ mod tests { assert_eq!(t.tv_sec as u64, 0x1_0000_0000_u64); } + #[cfg(any(libc, target_os = "wasi", windows))] + #[test] + fn test_to_timeval() { + let ts = Timespec { + tv_sec: 4, + tv_nsec: 999_999_500, + }; + let tv = ts.to_timeval().unwrap(); + assert_eq!(tv.tv_sec, 5); + assert_eq!(tv.tv_usec, 0); + + let ts2 = Timespec { + tv_sec: 2, + tv_nsec: 500_000_000, + }; + let tv2 = ts2.to_timeval().unwrap(); + assert_eq!(tv2.tv_sec, 2); + assert_eq!(tv2.tv_usec, 500_000); + } + // Test that our workarounds are needed. #[cfg(fix_y2038)] #[test] #[allow(deprecated)] fn test_fix_y2038() { - assert_eq_size!(libc::time_t, u32); + static_assertions::assert_eq_size!(libc::time_t, u32); } // Test that our workarounds are not needed. @@ -414,8 +471,8 @@ mod tests { #[cfg(linux_raw_dep)] #[test] fn test_against_kernel_timespec() { - assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec); - assert_eq_align!(Timespec, linux_raw_sys::general::__kernel_timespec); + static_assertions::assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec); + static_assertions::assert_eq_align!(Timespec, linux_raw_sys::general::__kernel_timespec); assert_eq!( memoffset::span_of!(Timespec, tv_sec), memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_sec) diff --git a/rust_backend/vendor/rustix-1.1.4/src/ugid.rs b/rust_backend/vendor/rustix-1.1.5/src/ugid.rs similarity index 94% rename from rust_backend/vendor/rustix-1.1.4/src/ugid.rs rename to rust_backend/vendor/rustix-1.1.5/src/ugid.rs index 11edb19b..6e5f5cf0 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/ugid.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/ugid.rs @@ -185,9 +185,9 @@ mod tests { #[test] fn test_sizes() { - assert_eq_size!(RawUid, u32); - assert_eq_size!(RawGid, u32); - assert_eq_size!(RawUid, libc::uid_t); - assert_eq_size!(RawGid, libc::gid_t); + static_assertions::assert_eq_size!(RawUid, u32); + static_assertions::assert_eq_size!(RawGid, u32); + static_assertions::assert_eq_size!(RawUid, libc::uid_t); + static_assertions::assert_eq_size!(RawGid, libc::gid_t); } } diff --git a/rust_backend/vendor/rustix-1.1.4/src/utils.rs b/rust_backend/vendor/rustix-1.1.5/src/utils.rs similarity index 95% rename from rust_backend/vendor/rustix-1.1.4/src/utils.rs rename to rust_backend/vendor/rustix-1.1.5/src/utils.rs index 77eb26c1..ba766377 100644 --- a/rust_backend/vendor/rustix-1.1.4/src/utils.rs +++ b/rust_backend/vendor/rustix-1.1.5/src/utils.rs @@ -68,7 +68,7 @@ macro_rules! default_union { core::mem::size_of_val(&u), core::mem::size_of_val(&field_value) ); - const_assert_eq!(memoffset::offset_of_union!($union, $field), 0); + static_assertions::const_assert_eq!(memoffset::offset_of_union!($union, $field), 0); } u diff --git a/rust_backend/vendor/rustix-1.1.4/src/weak.rs b/rust_backend/vendor/rustix-1.1.5/src/weak.rs similarity index 100% rename from rust_backend/vendor/rustix-1.1.4/src/weak.rs rename to rust_backend/vendor/rustix-1.1.5/src/weak.rs