Import 'libusb1-sys' crate
Request Document: go/android-rust-importing-crates
For CL Reviewers: go/android3p#cl-review
For Build Team: go/ab-third-party-imports
Bug: 376649133
Test: m liblibusb1-sys

Change-Id: I41f6c1d5b2163e06690d7ac1b90fab3a3f4559dc
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ce59553
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,63 @@
+package {
+    default_applicable_licenses: ["external_rust_crates_libusb1-sys_license"],
+    default_team: "trendy_team_android_rust",
+}
+
+license {
+    name: "external_rust_crates_libusb1-sys_license",
+    visibility: [":__subpackages__"],
+    license_kinds: ["SPDX-license-identifier-MIT"],
+    license_text: ["LICENSE"],
+}
+
+rust_library {
+    name: "liblibusb1_sys",
+    host_supported: true,
+    crate_name: "libusb1_sys",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.7.0",
+    crate_root: "src/lib.rs",
+    edition: "2018",
+    rustlibs: ["liblibc"],
+    shared_libs: ["libusb"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    vendor_available: true,
+}
+
+rust_test {
+    name: "libusb1-sys_test_src_lib",
+    host_supported: true,
+    crate_name: "libusb1_sys",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.7.0",
+    crate_root: "src/lib.rs",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    test_options: {
+        unit_test: true,
+    },
+    edition: "2018",
+    rustlibs: ["liblibc"],
+}
+
+rust_test {
+    name: "libusb1-sys_test_tests_test",
+    host_supported: true,
+    crate_name: "test",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.7.0",
+    crate_root: "tests/test.rs",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    test_options: {
+        unit_test: true,
+    },
+    edition: "2018",
+    rustlibs: [
+        "liblibc",
+        "liblibusb1_sys",
+    ],
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8f1acf6
--- /dev/null
+++ b/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/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..7743a4b
--- /dev/null
+++ b/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/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..67ecedb
--- /dev/null
+++ b/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 <[email protected]>",
+    "Ilya Averyanov <[email protected]>",
+]
+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/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..c4d4fb7
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,47 @@
+[package]
+
+name = "libusb1-sys"
+version = "0.7.0"
+authors = ["David Cuddeback <[email protected]>",
+            "Ilya Averyanov <[email protected]>"]
+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/LICENSE b/LICENSE
new file mode 100644
index 0000000..cf8ea43
--- /dev/null
+++ b/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/METADATA b/METADATA
new file mode 100644
index 0000000..3f30ab2
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "libusb1-sys"
+description: "FFI bindings for libusb."
+third_party {
+  identifier {
+    type: "crates.io"
+    value: "libusb1-sys"
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/libusb1-sys/libusb1-sys-0.7.0.crate"
+    primary_source: true
+  }
+  version: "0.7.0"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 11
+    day: 8
+  }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..4455493
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
[email protected]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bc4f2c6
--- /dev/null
+++ b/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/build.rs b/build.rs
new file mode 100644
index 0000000..bd1f43b
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,224 @@
+use std::{env, fs, path::PathBuf};
+
+static VERSION: &str = "1.0.27";
+
+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<usize> {
+    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/src/constants.rs b/src/constants.rs
new file mode 100644
index 0000000..a1d9551
--- /dev/null
+++ b/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::<libusb_control_setup>();
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..b0ac87d
--- /dev/null
+++ b/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<libusb_log_cb>, 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<libusb_pollfd_added_cb>,
+        removed_cb: Option<libusb_pollfd_removed_cb>,
+        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/tests/test.rs b/tests/test.rs
new file mode 100644
index 0000000..42cd5a0
--- /dev/null
+++ b/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::<ffi::libusb_transfer>::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::<ffi::libusb_transfer>::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);
+}