Full Code of ruabmbua/hidapi-rs for AI

main 719a5a896d7f cached
89 files
440.9 KB
253.8k tokens
432 symbols
1 requests
Download .txt
Showing preview only (468K chars total). Download the full file or copy to clipboard to get everything.
Repository: ruabmbua/hidapi-rs
Branch: main
Commit: 719a5a896d7f
Files: 89
Total size: 440.9 KB

Directory structure:
gitextract_v0dbt8oc/

├── .github/
│   └── workflows/
│       └── rust.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── Cargo.toml
├── LICENSE.txt
├── README.md
├── build.rs
├── examples/
│   ├── co2mon.rs
│   ├── dump_descriptors.rs
│   ├── logitech_gprox.rs
│   ├── lshid.rs
│   ├── open_first_device.rs
│   ├── readhid.rs
│   └── static_lifetime_bound.rs
├── src/
│   ├── error.rs
│   ├── ffi.rs
│   ├── hidapi/
│   │   ├── macos.rs
│   │   └── windows.rs
│   ├── hidapi.rs
│   ├── lib.rs
│   ├── linux_native/
│   │   └── ioctl.rs
│   ├── linux_native.rs
│   ├── macos.rs
│   ├── windows.rs
│   └── windows_native/
│       ├── descriptor/
│       │   ├── encoder.rs
│       │   ├── mod.rs
│       │   ├── tests.rs
│       │   ├── typedefs.rs
│       │   └── types.rs
│       ├── dev_node.rs
│       ├── device_info.rs
│       ├── error.rs
│       ├── hid.rs
│       ├── interfaces.rs
│       ├── mod.rs
│       ├── string.rs
│       ├── types.rs
│       └── utils.rs
└── tests/
    ├── assets/
    │   ├── mouse1.data
    │   └── mouse2.data
    └── pp_data/
        ├── 045E_02FF_0005_0001.expected
        ├── 045E_02FF_0005_0001.pp_data
        ├── 046A_0011_0006_0001.expected
        ├── 046A_0011_0006_0001.pp_data
        ├── 046D_0A37_0001_000C.expected
        ├── 046D_0A37_0001_000C.pp_data
        ├── 046D_B010_0001_000C.expected
        ├── 046D_B010_0001_000C.pp_data
        ├── 046D_B010_0001_FF00.expected
        ├── 046D_B010_0001_FF00.pp_data
        ├── 046D_B010_0002_0001.expected
        ├── 046D_B010_0002_0001.pp_data
        ├── 046D_B010_0002_FF00.expected
        ├── 046D_B010_0002_FF00.pp_data
        ├── 046D_B010_0006_0001.expected
        ├── 046D_B010_0006_0001.pp_data
        ├── 046D_C077_0002_0001.expected
        ├── 046D_C077_0002_0001.pp_data
        ├── 046D_C283_0004_0001.expected
        ├── 046D_C283_0004_0001.pp_data
        ├── 046D_C52F_0001_000C.expected
        ├── 046D_C52F_0001_000C.pp_data
        ├── 046D_C52F_0001_FF00.expected
        ├── 046D_C52F_0001_FF00.pp_data
        ├── 046D_C52F_0002_0001.expected
        ├── 046D_C52F_0002_0001.pp_data
        ├── 046D_C52F_0002_FF00.expected
        ├── 046D_C52F_0002_FF00.pp_data
        ├── 046D_C534_0001_000C.expected
        ├── 046D_C534_0001_000C.pp_data
        ├── 046D_C534_0001_FF00.expected
        ├── 046D_C534_0001_FF00.pp_data
        ├── 046D_C534_0002_0001.expected
        ├── 046D_C534_0002_0001.pp_data
        ├── 046D_C534_0002_FF00.expected
        ├── 046D_C534_0002_FF00.pp_data
        ├── 046D_C534_0006_0001.expected
        ├── 046D_C534_0006_0001.pp_data
        ├── 046D_C534_0080_0001.expected
        ├── 046D_C534_0080_0001.pp_data
        ├── 047F_C056_0001_000C.expected
        ├── 047F_C056_0001_000C.pp_data
        ├── 047F_C056_0003_FFA0.expected
        ├── 047F_C056_0003_FFA0.pp_data
        ├── 047F_C056_0005_000B.expected
        ├── 047F_C056_0005_000B.pp_data
        ├── 17CC_1130_0000_FF01.expected
        └── 17CC_1130_0000_FF01.pp_data

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/rust.yml
================================================
name: Rust

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always

jobs:
  build-linux:
    runs-on: ubuntu-latest

    env:
      DEBIAN_FRONTEND: noninteractive

    strategy:
      fail-fast: false # don't give up on the whole matrix if one variant fails
      matrix:
        linkage:
          - static
          - shared
        library:
          - hidraw
          - libusb

    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install dependencies
        run: |
          sudo apt-get update -y
          sudo apt-get install -y cmake libudev-dev libumockdev-dev umockdev
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build libusb
        run: |
          git clone https://github.com/libusb/libusb.git ./etc/libusb/
          cd ./etc/libusb/
          ./autogen.sh
          make
          sudo make install
          cd -
          rm -rf ./etc/libusb/
      - name: Build hidapi
        run: |
          cd ./etc/hidapi/
          mkdir ./build/
          cd ./build/
          cmake ..
          make
          sudo make install
      - name: List pkgconfig definitions
        run: grep -RHn ^ /usr/local/lib/pkgconfig
      - name: Build
        run: cargo build --no-default-features --features linux-${{ matrix.linkage }}-${{ matrix.library }} --verbose
      - name: Run tests
        run: cargo test --no-default-features --features linux-${{ matrix.linkage }}-${{ matrix.library }} --verbose
      - name: Verify package
        run: |
          cargo package --no-default-features --features linux-${{ matrix.linkage }}-${{ matrix.library }} --verbose

  build-linux-native:
    runs-on: ubuntu-latest
    env:
      DEBIAN_FRONTEND: noninteractive
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install dependencies
        run: |
          sudo apt-get update -y
          sudo apt-get install -y libudev-dev
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build
        run: cargo build --no-default-features --features linux-native --verbose
      - name: Run tests
        run: cargo test --no-default-features --features linux-native --verbose
      - name: Verify package
        run: cargo package --no-default-features --features linux-native --verbose

  build-linux-native-basic-udev:
    runs-on: ubuntu-latest
    env:
      DEBIAN_FRONTEND: noninteractive
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install dependencies
        run: |
          sudo apt-get update -y
          sudo apt-get install -y libudev-dev
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build
        run: cargo build --no-default-features --features linux-native-basic-udev --verbose
      - name: Run tests
        run: cargo test --no-default-features --features linux-native-basic-udev --verbose
      - name: Verify package
        run: cargo package --no-default-features --features linux-native-basic-udev --verbose

  build-windows:
    runs-on: windows-latest
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build
        run: cargo build --no-default-features --verbose
      - name: Run tests
        run: cargo test --no-default-features --verbose
      - name: Verify package
        run: cargo package --no-default-features --verbose

  build-windows-native:
    runs-on: windows-latest
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build
        run: cargo build --no-default-features --features windows-native --verbose
      - name: Run tests
        run: cargo test --no-default-features --features windows-native --verbose
      - name: Verify package
        run: cargo package --no-default-features --features windows-native --verbose

  build-linux-on-windows:
    runs-on: windows-latest
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Install Linux target
        run: rustup target add arm-unknown-linux-musleabihf
      - name: Build
        run: cargo build --target arm-unknown-linux-musleabihf --no-default-features --features linux-native-basic-udev --verbose
      - name: Verify package
        run: cargo package --target arm-unknown-linux-musleabihf --no-default-features --features linux-native-basic-udev --verbose

  build-macos:
    runs-on: macos-latest
    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Build
        run: cargo build --no-default-features --verbose
      - name: Run tests
        run: cargo test --no-default-features --verbose
      - name: Verify package
        run: cargo package --no-default-features --verbose

  fmt-check:
    runs-on: ubuntu-latest

    env:
      DEBIAN_FRONTEND: noninteractive

    steps:
      - name: Checkout repository and submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - name: Check code formatting
        run: cargo fmt --check


================================================
FILE: .gitignore
================================================
target
Cargo.lock
.idea/
hidapi-rs.iml
.helix/
.vscode/


================================================
FILE: .gitmodules
================================================
[submodule "etc/hidapi"]
	path = etc/hidapi
	url = https://github.com/libusb/hidapi.git


================================================
FILE: .travis.yml
================================================
language: rust


matrix:
  include:
    - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-libusb"

    - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-static-hidraw"

    - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-libusb"

    - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS="linux-shared-hidraw"

    - env: TARGET=x86_64-apple-darwin
      os: osx


script:
  - cargo build --verbose --no-default-features --features="${FEATURE_FLAGS}"
  - cargo test --verbose --no-default-features --features="${FEATURE_FLAGS}"

addons:
  apt:
    packages:
      - libusb-1.0-0-dev
      - libudev-dev
      - libhidapi-dev

================================================
FILE: Cargo.toml
================================================
[package]
name = "hidapi"
version = "2.6.5"
authors = [
    "Roland Ruckerbauer <mail@ruabmbua.dev>",
    "Osspial <osspial@gmail.com>",
    "Artyom Pavlov <newpavlov@gmail.com>",
    "mberndt123",
    "niklasad1",
    "Stefan Kerkmann"
]
repository = "https://github.com/ruabmbua/hidapi-rs"
description = "Rust-y wrapper around hidapi"
license = "MIT"
keywords = ["hid", "api", "usb", "binding", "wrapper"]
build = "build.rs"
links = "hidapi"
documentation = "https://docs.rs/hidapi"
edition = "2021"
include = [
    "README.md",
    "LICENSE.txt",
    "build.rs",
    "/src",
    "/etc/hidapi/CMakeLists.txt",
    "/etc/hidapi/LICENSE*",
    "/etc/hidapi/VERSION",
    "/etc/hidapi/hidapi",
    "/etc/hidapi/libusb",
    "/etc/hidapi/src",
    "/etc/hidapi/udev",
    # Platform support files
    "/etc/hidapi/linux/CMakeLists.txt",
    "/etc/hidapi/linux/*.c",
    "/etc/hidapi/linux/*.h",
    "/etc/hidapi/mac/CMakeLists.txt",
    "/etc/hidapi/mac/*.c",
    "/etc/hidapi/mac/*.h",
    "/etc/hidapi/windows/CMakeLists.txt",
    "/etc/hidapi/windows/*.c",
    "/etc/hidapi/windows/*.h",
]

[features]
default = ["linux-static-hidraw", "illumos-static-libusb"]
linux-static-libusb = []
linux-static-hidraw = []
linux-shared-libusb = []
linux-shared-hidraw = []
linux-native = ["dep:udev", "dep:nix"]
linux-native-basic-udev = ["dep:basic-udev", "dep:nix"]
illumos-static-libusb = []
illumos-shared-libusb = []
macos-shared-device = []
windows-native = [
    "windows-sys/Win32_Devices_DeviceAndDriverInstallation",
    "windows-sys/Win32_Devices_HumanInterfaceDevice",
    "windows-sys/Win32_Devices_Properties",
    "windows-sys/Win32_Security",
    "windows-sys/Win32_Storage_FileSystem",
    "windows-sys/Win32_System_IO",
    "windows-sys/Win32_System_Threading",
    "windows-sys/Win32_UI_Shell_PropertiesSystem"
]

[dependencies]
libc = "0.2"
cfg-if = "1"

[target.'cfg(target_os = "linux")'.dependencies]
udev = { version = "0.9", optional = true }
basic-udev = { version = "0.1", optional = true }
nix = { version = "0.30", optional = true, features = ["fs", "ioctl", "poll"] }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_Foundation"] }

[build-dependencies]
cc = "1.2"
pkg-config = "0.3"

[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]


================================================
FILE: LICENSE.txt
================================================
Copyright 2017 The hidapi-rs Developers

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.


================================================
FILE: README.md
================================================
# hidapi [![Version](https://img.shields.io/crates/v/hidapi.svg)](https://crates.io/crates/hidapi) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Osspial/hidapi-rs/blob/master/LICENSE.txt) [![Documentation](https://docs.rs/hidapi/badge.svg)](https://docs.rs/hidapi) [![Chat](https://img.shields.io/badge/discord-devroom-blue.svg)](https://discordapp.com/invite/3ahhJGN)

This crate provides a rust abstraction over the features of the C library
[hidapi](https://github.com/libusb/hidapi). Based off of
[hidapi-rs](https://github.com/Osspial/hidapi-rs) by Osspial.

# Usage

This crate is on [crates.io](https://crates.io/crates/hidapi) and can be
used by adding `hidapi` to the dependencies in your project's `Cargo.toml`.

In case you are downloading / checking out this repository directly via git,
make sure to initialize the git submodules after cloning!

```sh
git submodule update --init
```

# Example

```rust
extern crate hidapi;

let api = hidapi::HidApi::new().unwrap();
// Print out information about all connected devices
for device in api.device_list() {
    println!("{:#?}", device);
}

// Connect to device using its VID and PID
let (VID, PID) = (0x0123, 0x3456);
let device = api.open(VID, PID).unwrap();

// Read data from device
let mut buf = [0u8; 8];
let res = device.read(&mut buf[..]).unwrap();
println!("Read: {:?}", &buf[..res]);

// Write data to device
let buf = [0u8, 1, 2, 3, 4];
let res = device.write(&buf).unwrap();
println!("Wrote: {:?} byte(s)", res);
```

# Documentation
Available at [docs.rs](https://docs.rs/hidapi).


================================================
FILE: build.rs
================================================
// **************************************************************************
// Copyright (c) 2015 Roland Ruckerbauer All Rights Reserved.
//
// This file is part of hidapi_rust.
//
// hidapi_rust is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// hidapi_rust 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hidapi_rust.  If not, see <http://www.gnu.org/licenses/>.
// *************************************************************************

extern crate cc;
extern crate pkg_config;

use std::env;

fn main() {
    let target = env::var("TARGET").unwrap();

    println!("cargo:rustc-check-cfg=cfg(hidapi)");
    println!("cargo:rustc-check-cfg=cfg(libusb)");

    if target.contains("linux") {
        compile_linux();
    } else if target.contains("windows") {
        compile_windows();
    } else if target.contains("darwin") {
        compile_macos();
    } else if target.contains("freebsd") {
        compile_freebsd();
    } else if target.contains("openbsd") {
        compile_openbsd();
    } else if target.contains("illumos") {
        compile_illumos();
    } else {
        panic!("Unsupported target os for hidapi-rs");
    }
}

fn compile_linux() {
    // First check the features enabled for the crate.
    // Only one linux backend should be enabled at a time.

    let avail_backends: [(&'static str, &dyn Fn()); 6] = [
        ("LINUX_STATIC_HIDRAW", &|| {
            let mut config = cc::Build::new();
            println!("cargo:rerun-if-changed=etc/hidapi/linux/hid.c");
            config
                .file("etc/hidapi/linux/hid.c")
                .include("etc/hidapi/hidapi");
            pkg_config::probe_library("libudev").expect("Unable to find libudev");
            config.compile("libhidapi.a");
            println!("cargo:rustc-cfg=hidapi");
        }),
        ("LINUX_STATIC_LIBUSB", &|| {
            let mut config = cc::Build::new();
            println!("cargo:rerun-if-changed=etc/hidapi/linux/hid.c");
            config
                .file("etc/hidapi/libusb/hid.c")
                .include("etc/hidapi/hidapi");
            let lib = pkg_config::find_library("libusb-1.0").expect("Unable to find libusb-1.0");
            for path in lib.include_paths {
                config.include(
                    path.to_str()
                        .expect("Failed to convert include path to str"),
                );
            }
            config.compile("libhidapi.a");
            println!("cargo:rustc-cfg=libusb");
            println!("cargo:rustc-cfg=hidapi");
        }),
        ("LINUX_SHARED_HIDRAW", &|| {
            pkg_config::probe_library("hidapi-hidraw").expect("Unable to find hidapi-hidraw");
            println!("cargo:rustc-cfg=hidapi");
        }),
        ("LINUX_SHARED_LIBUSB", &|| {
            pkg_config::probe_library("libusb-1.0").expect("Unable to find libusb-1.0");
            pkg_config::probe_library("hidapi-libusb").expect("Unable to find hidapi-libusb");
            println!("cargo:rustc-cfg=libusb");
            println!("cargo:rustc-cfg=hidapi");
        }),
        ("LINUX_NATIVE", &|| {
            // The udev crate takes care of finding its library
        }),
        ("LINUX_NATIVE_BASIC_UDEV", &|| {
            // Enable `feature="linux-native"` to reuse the existing
            // linux-native code. It is considered an error in
            // basic-udev if this fails to compile.
            println!("cargo:rustc-cfg=feature=\"linux-native\"");
        }),
    ];

    let mut backends = avail_backends
        .iter()
        .filter(|f| env::var(format!("CARGO_FEATURE_{}", f.0)).is_ok());

    if backends.clone().count() != 1 {
        panic!("Exactly one linux hidapi backend must be selected.");
    }

    // Build it!
    (backends.next().unwrap().1)();
}

//#[cfg(all(feature = "shared-libusb", not(feature = "shared-hidraw")))]
//fn compile_linux() {
//
//}
//
//#[cfg(all(feature = "shared-hidraw"))]
//fn compile_linux() {
//
//}

fn compile_freebsd() {
    pkg_config::probe_library("hidapi").expect("Unable to find hidapi");
    println!("cargo:rustc-cfg=libusb");
    println!("cargo:rustc-cfg=hidapi");
}

fn compile_openbsd() {
    pkg_config::probe_library("hidapi-libusb").expect("Unable to find hidapi");
    println!("cargo:rustc-cfg=libusb");
    println!("cargo:rustc-cfg=hidapi");
}

fn compile_illumos() {
    // First check the features enabled for the crate.
    // Only one illumos backend should be enabled at a time.

    let avail_backends: [(&'static str, &dyn Fn()); 2] = [
        ("ILLUMOS_STATIC_LIBUSB", &|| {
            let mut config = cc::Build::new();
            config
                .file("etc/hidapi/libusb/hid.c")
                .include("etc/hidapi/hidapi");
            let lib = pkg_config::find_library("libusb-1.0").expect("Unable to find libusb-1.0");
            for path in lib.include_paths {
                config.include(
                    path.to_str()
                        .expect("Failed to convert include path to str"),
                );
            }
            config.compile("libhidapi.a");
        }),
        ("ILLUMOS_SHARED_LIBUSB", &|| {
            pkg_config::probe_library("hidapi-libusb").expect("Unable to find hidapi-libusb");
        }),
    ];

    let mut backends = avail_backends
        .iter()
        .filter(|f| env::var(format!("CARGO_FEATURE_{}", f.0)).is_ok());

    if backends.clone().count() != 1 {
        panic!("Exactly one illumos hidapi backend must be selected.");
    }

    // Build it!
    (backends.next().unwrap().1)();

    println!("cargo:rustc-cfg=libusb");
    println!("cargo:rustc-cfg=hidapi");
}

fn compile_windows() {
    #[cfg(not(feature = "windows-native"))]
    {
        let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();

        let mut cc = cc::Build::new();
        cc.file("etc/hidapi/windows/hid.c")
            .include("etc/hidapi/hidapi");

        if linkage.contains("crt-static") {
            // https://doc.rust-lang.org/reference/linkage.html#static-and-dynamic-c-runtimes
            cc.static_crt(true);
        }
        cc.compile("libhidapi.a");
        println!("cargo:rustc-link-lib=setupapi");

        println!("cargo:rustc-cfg=hidapi");
    }
}

fn compile_macos() {
    cc::Build::new()
        .file("etc/hidapi/mac/hid.c")
        .include("etc/hidapi/hidapi")
        .compile("libhidapi.a");
    println!("cargo:rustc-cfg=hidapi");
    println!("cargo:rustc-link-lib=framework=IOKit");
    println!("cargo:rustc-link-lib=framework=CoreFoundation");
    println!("cargo:rustc-link-lib=framework=AppKit")
}


================================================
FILE: examples/co2mon.rs
================================================
/****************************************************************************
    Copyright (c) 2015 Artyom Pavlov All Rights Reserved.

    This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
    It's also based on the Oleg Bulatov's work (https://github.com/dmage/co2mon)
****************************************************************************/

//! Opens a KIT MT 8057 CO2 detector and reads data from it. This
//! example will not work unless such device is plugged into your system.

use hidapi::{HidApi, HidError};
use std::io;

const CODE_TEMPERATURE: u8 = 0x42;
const CODE_CONCENTRATION: u8 = 0x50;
const HID_TIMEOUT: i32 = 5000;
const DEV_VID: u16 = 0x04d9;
const DEV_PID: u16 = 0xa052;
const PACKET_SIZE: usize = 8;

type Packet = [u8; PACKET_SIZE];

enum CO2Result {
    Temperature(f32),
    Concentration(u16),
    Unknown(u8, u16),
    Error(&'static str),
}

fn decode_temperature(value: u16) -> f32 {
    (value as f32) * 0.0625 - 273.15
}

fn decrypt(buf: Packet) -> Packet {
    let mut res: [u8; PACKET_SIZE] = [
        (buf[3] << 5) | (buf[2] >> 3),
        (buf[2] << 5) | (buf[4] >> 3),
        (buf[4] << 5) | (buf[0] >> 3),
        (buf[0] << 5) | (buf[7] >> 3),
        (buf[7] << 5) | (buf[1] >> 3),
        (buf[1] << 5) | (buf[6] >> 3),
        (buf[6] << 5) | (buf[5] >> 3),
        (buf[5] << 5) | (buf[3] >> 3),
    ];

    let magic_word = b"Htemp99e";
    for i in 0..PACKET_SIZE {
        let sub_val: u8 = magic_word[i].rotate_right(4);
        res[i] = u8::wrapping_sub(res[i], sub_val);
    }

    res
}

fn decode_buf(buf: Packet) -> CO2Result {
    // Do we need to decrypt the data?
    let res = if buf[4] == 0x0d { buf } else { decrypt(buf) };

    let kind = res[0];
    let val = u16::from_be_bytes(res[1..3].try_into().unwrap());
    let checksum = res[3];
    let tail = res[4];

    if tail != 0x0d {
        return CO2Result::Error("Unexpected data (data[4] != 0x0d)");
    }
    let checksum_calc = res[0].wrapping_add(res[1]).wrapping_add(res[2]);
    if checksum != checksum_calc {
        return CO2Result::Error("Checksum error");
    }

    match kind {
        CODE_TEMPERATURE => CO2Result::Temperature(decode_temperature(val)),
        CODE_CONCENTRATION => {
            if val > 3000 {
                CO2Result::Error("Concentration bigger than 3000 (uninitialized device?)")
            } else {
                CO2Result::Concentration(val)
            }
        }
        _ => CO2Result::Unknown(kind, val),
    }
}

fn invalid_data_err(msg: impl Into<String>) -> HidError {
    HidError::IoError {
        error: io::Error::new(io::ErrorKind::InvalidData, msg.into()),
    }
}

fn main() -> Result<(), HidError> {
    let api = HidApi::new()?;
    let dev = api.open(DEV_VID, DEV_PID)?;
    dev.send_feature_report(&[0; PACKET_SIZE])?;

    if let Some(manufacturer) = dev.get_manufacturer_string()? {
        println!("Manufacurer:\t{manufacturer}");
    }
    if let Some(product) = dev.get_product_string()? {
        println!("Product:\t{product}");
    }
    if let Some(serial_number) = dev.get_serial_number_string()? {
        println!("Serial number:\t{serial_number}");
    }

    let mut buf = [0; PACKET_SIZE];
    loop {
        let n = dev.read_timeout(&mut buf[..], HID_TIMEOUT)?;
        if n != PACKET_SIZE {
            let msg = format!("unexpected packet length: {n}/{PACKET_SIZE}");
            return Err(invalid_data_err(msg));
        }
        match decode_buf(buf) {
            CO2Result::Temperature(val) => println!("Temp:\t{val}"),
            CO2Result::Concentration(val) => println!("Conc:\t{val}"),
            CO2Result::Unknown(kind, val) => eprintln!("Unknown({kind}):\t{val}"),
            CO2Result::Error(msg) => {
                return Err(invalid_data_err(msg));
            }
        }
    }
}


================================================
FILE: examples/dump_descriptors.rs
================================================
use hidapi::HidApi;

fn main() {
    println!("Printing all available hid devices:");

    match HidApi::new() {
        Ok(api) => {
            for device in api.device_list() {
                println!(
                    "  {} (Interface {}):",
                    device.product_string().unwrap_or("<COULD NOT FETCH>"),
                    device.interface_number()
                );
                let mut descriptor = vec![0u8; 2048];
                match device
                    .open_device(&api)
                    .and_then(|dev| dev.get_report_descriptor(&mut descriptor))
                {
                    Ok(length) => println!("    {:?}", &mut descriptor[..length]),
                    Err(err) => println!("    Failed to retrieve descriptor ({:?})", err),
                }
            }
        }
        Err(e) => {
            eprintln!("Error: {}", e);
        }
    }
}


================================================
FILE: examples/logitech_gprox.rs
================================================
/****************************************************************************
    Copyright (c) 2022 ruabmbua All Rights Reserved.
****************************************************************************/

//! Sets the sidechannel volume of the logitech gpro x headset

extern crate hidapi;

use hidapi::HidApi;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let vol = std::env::args()
        .nth(1)
        .map(|arg| arg.parse::<u8>())
        .ok_or("missing sidechannel volume arg")??
        .min(100);

    let api = HidApi::new()?;
    let dev = api.open(0x046d, 0x0aaa)?;

    println!("Setting sidechannel volume to {}", vol);

    dev.write(&[0x11, 0xff, 0x05, 0x1c, vol])?;

    Ok(())
}


================================================
FILE: examples/lshid.rs
================================================
/****************************************************************************
    Copyright (c) 2015 Osspial All Rights Reserved.

    This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
****************************************************************************/

//! Prints out a list of HID devices

extern crate hidapi;

use hidapi::HidApi;

fn main() {
    println!("Printing all available hid devices:");

    match HidApi::new() {
        Ok(api) => {
            for device in api.device_list() {
                println!(
                    "VID: {:04x}, PID: {:04x}, Serial: {}, Product name: {}, Interface: {}",
                    device.vendor_id(),
                    device.product_id(),
                    device.serial_number().unwrap_or("<COULD NOT FETCH>"),
                    device.product_string().unwrap_or("<COULD NOT FETCH>"),
                    device.interface_number()
                );
            }
        }
        Err(e) => {
            eprintln!("Error: {}", e);
        }
    }
}


================================================
FILE: examples/open_first_device.rs
================================================
/****************************************************************************
    Copyright (c) 2018 Roland Ruckerbauer All Rights Reserved.

    This file is part of hidapi-rs, based on hidapi-rs by Osspial
****************************************************************************/

//! Opens the first hid device it can find, and reads data in a blocking fashion
//! from it in an endless loop.

extern crate hidapi;

use hidapi::{HidApi, HidError};

fn main() {
    fn run() -> Result<(), HidError> {
        let hidapi = HidApi::new()?;

        let device_info = hidapi
            .device_list()
            .next()
            .expect("No devices are available!")
            .clone();

        println!(
            "Opening device:\n VID: {:04x}, PID: {:04x}\n",
            device_info.vendor_id(),
            device_info.product_id()
        );

        let device = device_info.open_device(&hidapi)?;

        let mut buf = vec![0; 64];

        println!("Reading data from device ...\n");

        loop {
            let len = device.read(&mut buf)?;
            println!("{:?}", &buf[..len]);
        }
    }

    if let Err(e) = run() {
        eprintln!("Error: {}", e);
    }
}


================================================
FILE: examples/readhid.rs
================================================
/****************************************************************************
    Copyright (c) 2015 Osspial All Rights Reserved.

    This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
****************************************************************************/

//! Opens a Thrustmaster T-Flight HOTAS X HID and reads data from it. This
//! example will not work unless such an HID is plugged in to your system.
//! Will update in the future to support all HIDs.

extern crate hidapi;

use hidapi::HidApi;

fn main() {
    let api = HidApi::new().expect("Failed to create API instance");

    let joystick = api.open(1103, 45320).expect("Failed to open device");

    loop {
        let mut buf = [0u8; 256];
        let res = joystick.read(&mut buf[..]).unwrap();

        let mut data_string = String::new();

        for u in &buf[..res] {
            data_string.push_str(&(u.to_string() + "\t"));
        }

        println!("{}", data_string);
    }
}


================================================
FILE: examples/static_lifetime_bound.rs
================================================
/****************************************************************************
Copyright (c) 2015 Osspial All Rights Reserved.

This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
****************************************************************************/

//! This example shows the added possibility (after version 0.4.1),
//! to move devices into a function / or closure with static lifetime bounds.

extern crate hidapi;

use hidapi::{HidApi, HidDevice};
use std::rc::Rc;

fn main() {
    let _dev = test_lt();
}

fn requires_static_lt_bound<F: Fn() + 'static>(f: F) {
    f();
}

fn test_lt() -> Rc<HidDevice> {
    let api = HidApi::new().expect("Hidapi init failed");

    let mut devices = api.device_list();

    let dev_info = devices
        .next()
        .expect("There is not a single hid device available");

    let dev = Rc::new(
        api.open(dev_info.vendor_id(), dev_info.product_id())
            .expect("Can not open device"),
    );

    let dev_1 = dev.clone();
    requires_static_lt_bound(move || {
        println!("{:?}", dev_1.get_device_info().unwrap()); //<! Can be captured by closure with static lt
    });

    dev //<! Can be returned from a function, which exceeds the lifetime of the API context
}


================================================
FILE: src/error.rs
================================================
// **************************************************************************
// Copyright (c) 2018 Roland Ruckerbauer All Rights Reserved.
//
// This file is part of hidapi-rs, based on hidapi-rs by Osspial
// **************************************************************************

use libc::wchar_t;
use std::error::Error;
use std::fmt::{Display, Formatter, Result};

use crate::DeviceInfo;

#[derive(Debug)]
pub enum HidError {
    HidApiError {
        message: String,
    },
    HidApiErrorEmpty,
    FromWideCharError {
        wide_char: wchar_t,
    },
    InitializationError,
    InvalidZeroSizeData,
    IncompleteSendError {
        sent: usize,
        all: usize,
    },
    SetBlockingModeError {
        mode: &'static str,
    },
    OpenHidDeviceWithDeviceInfoError {
        device_info: Box<DeviceInfo>,
    },
    /// An IO error or a system error that can be represented as such
    IoError {
        error: std::io::Error,
    },
}

impl Display for HidError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        match self {
            HidError::HidApiError { message } => write!(f, "hidapi error: {}", message),
            HidError::HidApiErrorEmpty => write!(f, "hidapi error: (could not get error message)"),
            HidError::FromWideCharError { wide_char } => {
                write!(f, "failed converting {:#X} to rust char", wide_char)
            }
            HidError::InitializationError => {
                write!(f, "Failed to initialize hidapi")
            }
            HidError::InvalidZeroSizeData => write!(f, "Invalid data: size can not be 0"),
            HidError::IncompleteSendError { sent, all } => write!(
                f,
                "Failed to send all data: only sent {} out of {} bytes",
                sent, all
            ),
            HidError::SetBlockingModeError { mode } => {
                write!(f, "Can not set blocking mode to '{}'", mode)
            }
            HidError::OpenHidDeviceWithDeviceInfoError { device_info } => {
                write!(f, "Can not open hid device with: {:?}", *device_info)
            }
            HidError::IoError { error } => {
                write!(f, "{error}")
            }
        }
    }
}

impl Error for HidError {}

impl From<std::io::Error> for HidError {
    fn from(e: std::io::Error) -> Self {
        Self::IoError { error: e }
    }
}

#[cfg(all(feature = "linux-native", target_os = "linux"))]
impl From<nix::errno::Errno> for HidError {
    fn from(e: nix::errno::Errno) -> Self {
        Self::IoError { error: e.into() }
    }
}


================================================
FILE: src/ffi.rs
================================================
#![allow(unused_imports, dead_code)]

/// **************************************************************************
/// Copyright (c) 2015 Osspial All Rights Reserved.
///
/// This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
/// *************************************************************************
// For documentation look at the corresponding C header file hidapi.h
use libc::{c_char, c_int, c_uchar, c_ushort, c_void, intptr_t, size_t, wchar_t};
type HidBusType = crate::BusType;
pub type HidDevice = c_void;
type LibusbContext = c_void;

#[repr(C)]
pub struct HidDeviceInfo {
    pub path: *mut c_char,
    pub vendor_id: c_ushort,
    pub product_id: c_ushort,
    pub serial_number: *mut wchar_t,
    pub release_number: c_ushort,
    pub manufacturer_string: *mut wchar_t,
    pub product_string: *mut wchar_t,
    pub usage_page: c_ushort,
    pub usage: c_ushort,
    pub interface_number: c_int,
    pub next: *mut HidDeviceInfo,
    pub bus_type: HidBusType,
}

#[allow(dead_code)]
extern "C" {
    #[cfg_attr(target_os = "openbsd", link_name = "hidapi_hid_init")]
    pub fn hid_init() -> c_int;
    pub fn hid_exit() -> c_int;
    pub fn hid_enumerate(vendor_id: c_ushort, product_id: c_ushort) -> *mut HidDeviceInfo;
    pub fn hid_free_enumeration(hid_device_info: *mut HidDeviceInfo);
    pub fn hid_open(
        vendor_id: c_ushort,
        product_id: c_ushort,
        serial_number: *const wchar_t,
    ) -> *mut HidDevice;
    pub fn hid_open_path(path: *const c_char) -> *mut HidDevice;
    #[cfg(libusb)]
    pub fn hid_libusb_wrap_sys_device(sys_dev: intptr_t, interface_num: c_int) -> *mut HidDevice;
    #[cfg(all(libusb, not(target_os = "freebsd")))]
    pub fn libusb_set_option(ctx: *mut LibusbContext, option: c_int);
    pub fn hid_write(device: *mut HidDevice, data: *const c_uchar, length: size_t) -> c_int;
    pub fn hid_read_timeout(
        device: *mut HidDevice,
        data: *mut c_uchar,
        length: size_t,
        milleseconds: c_int,
    ) -> c_int;
    pub fn hid_read(device: *mut HidDevice, data: *mut c_uchar, length: size_t) -> c_int;
    pub fn hid_set_nonblocking(device: *mut HidDevice, nonblock: c_int) -> c_int;
    pub fn hid_send_feature_report(
        device: *mut HidDevice,
        data: *const c_uchar,
        length: size_t,
    ) -> c_int;
    pub fn hid_get_feature_report(
        device: *mut HidDevice,
        data: *mut c_uchar,
        length: size_t,
    ) -> c_int;
    pub fn hid_send_output_report(
        device: *mut HidDevice,
        data: *const c_uchar,
        length: size_t,
    ) -> c_int;
    pub fn hid_get_input_report(
        device: *mut HidDevice,
        data: *mut c_uchar,
        length: size_t,
    ) -> c_int;
    pub fn hid_close(device: *mut HidDevice);
    pub fn hid_get_manufacturer_string(
        device: *mut HidDevice,
        string: *mut wchar_t,
        maxlen: size_t,
    ) -> c_int;
    pub fn hid_get_product_string(
        device: *mut HidDevice,
        string: *mut wchar_t,
        maxlen: size_t,
    ) -> c_int;
    pub fn hid_get_serial_number_string(
        device: *mut HidDevice,
        string: *mut wchar_t,
        maxlen: size_t,
    ) -> c_int;
    pub fn hid_get_device_info(device: *mut HidDevice) -> *mut HidDeviceInfo;
    pub fn hid_get_indexed_string(
        device: *mut HidDevice,
        string_index: c_int,
        string: *mut wchar_t,
        maxlen: size_t,
    ) -> c_int;
    pub fn hid_get_report_descriptor(
        hid_device: *mut HidDevice,
        buf: *mut c_uchar,
        buf_size: size_t,
    ) -> c_int;
    pub fn hid_error(device: *mut HidDevice) -> *const wchar_t;
}

// For documentation look at the corresponding C header file hidapi_darwin.h
#[cfg(target_os = "macos")]
pub mod macos {
    use super::*;

    extern "C" {
        pub fn hid_darwin_get_location_id(device: *mut HidDevice, location_id: *mut u32) -> c_int;
        pub fn hid_darwin_set_open_exclusive(open_exclusive: c_int);
        pub fn hid_darwin_get_open_exclusive() -> c_int;
        pub fn hid_darwin_is_device_open_exclusive(device: *mut HidDevice) -> c_int;
    }
}

// For documentation look at the corresponding C header file hidapi_winapi.h
#[cfg(target_os = "windows")]
pub mod windows {
    use super::*;
    use windows_sys::core::GUID;

    extern "C" {
        pub fn hid_winapi_get_container_id(
            device: *mut HidDevice,
            container_id: *mut GUID,
        ) -> c_int;

    }
}


================================================
FILE: src/hidapi/macos.rs
================================================
//! The extra beahviour for macOS

use super::HidDevice;
use crate::{ffi, HidDeviceBackendBase, HidDeviceBackendMacos, HidResult};

impl HidDeviceBackendMacos for HidDevice {
    fn get_location_id(&self) -> HidResult<u32> {
        let mut location_id: u32 = 0;

        let res = unsafe {
            ffi::macos::hid_darwin_get_location_id(self._hid_device, &mut location_id as *mut u32)
        };

        if res == -1 {
            match self.check_error() {
                Ok(err) => Err(err),
                Err(err) => Err(err),
            }
        } else {
            Ok(location_id)
        }
    }

    fn is_open_exclusive(&self) -> HidResult<bool> {
        let res = unsafe { ffi::macos::hid_darwin_is_device_open_exclusive(self._hid_device) };

        if res == -1 {
            match self.check_error() {
                Ok(err) => Err(err),
                Err(err) => Err(err),
            }
        } else {
            Ok(res == 1)
        }
    }
}


================================================
FILE: src/hidapi/windows.rs
================================================
//! The extra behaviour for Windows

use std::ptr::addr_of_mut;
use windows_sys::core::GUID;

use super::HidDevice;
use crate::{ffi, HidDeviceBackendBase, HidDeviceBackendWindows, HidResult};

impl HidDeviceBackendWindows for HidDevice {
    fn get_container_id(&self) -> HidResult<GUID> {
        let mut container_id: GUID = unsafe { std::mem::zeroed() };

        let res = unsafe {
            ffi::windows::hid_winapi_get_container_id(self._hid_device, addr_of_mut!(container_id))
        };

        if res == -1 {
            match self.check_error() {
                Ok(err) => Err(err),
                Err(err) => Err(err),
            }
        } else {
            Ok(container_id)
        }
    }
}


================================================
FILE: src/hidapi.rs
================================================
//! The implementation which uses the C library to perform operations

use std::{
    ffi::CStr,
    fmt::{self, Debug},
};

use libc::{c_int, size_t, wchar_t};

use crate::{ffi, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
mod windows;

const STRING_BUF_LEN: usize = 128;

pub struct HidApiBackend;

impl HidApiBackend {
    pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
        let mut device_vector = Vec::with_capacity(8);

        let enumeration = unsafe { ffi::hid_enumerate(vid, pid) };
        {
            let mut current_device = enumeration;

            while !current_device.is_null() {
                device_vector.push(unsafe { conv_hid_device_info(current_device)? });
                current_device = unsafe { (*current_device).next };
            }
        }

        if !enumeration.is_null() {
            unsafe { ffi::hid_free_enumeration(enumeration) };
        }

        Ok(device_vector)
    }

    pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
        let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) };

        if device.is_null() {
            match Self::check_error() {
                Ok(err) => Err(err),
                Err(e) => Err(e),
            }
        } else {
            Ok(HidDevice::from_raw(device))
        }
    }

    pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
        let mut chars = sn.chars().map(|c| c as wchar_t).collect::<Vec<_>>();
        chars.push(0 as wchar_t);
        let device = unsafe { ffi::hid_open(vid, pid, chars.as_ptr()) };
        if device.is_null() {
            match Self::check_error() {
                Ok(err) => Err(err),
                Err(e) => Err(e),
            }
        } else {
            Ok(HidDevice::from_raw(device))
        }
    }

    pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
        let device = unsafe { ffi::hid_open_path(device_path.as_ptr()) };

        if device.is_null() {
            match Self::check_error() {
                Ok(err) => Err(err),
                Err(e) => Err(e),
            }
        } else {
            Ok(HidDevice::from_raw(device))
        }
    }

    pub fn check_error() -> HidResult<HidError> {
        Ok(HidError::HidApiError {
            message: unsafe {
                match wchar_to_string(ffi::hid_error(std::ptr::null_mut())) {
                    WcharString::String(s) => s,
                    _ => return Err(HidError::HidApiErrorEmpty),
                }
            },
        })
    }
}

/// Converts a pointer to a `*const wchar_t` to a WcharString.
unsafe fn wchar_to_string(wstr: *const wchar_t) -> WcharString {
    if wstr.is_null() {
        return WcharString::None;
    }

    let mut char_vector: Vec<char> = Vec::with_capacity(8);
    let mut raw_vector: Vec<wchar_t> = Vec::with_capacity(8);
    let mut index: isize = 0;
    let mut invalid_char = false;

    let o = |i| *wstr.offset(i);

    while o(index) != 0 {
        use std::char;

        raw_vector.push(*wstr.offset(index));

        if !invalid_char {
            if let Some(c) = char::from_u32(o(index) as u32) {
                char_vector.push(c);
            } else {
                invalid_char = true;
            }
        }

        index += 1;
    }

    if !invalid_char {
        WcharString::String(char_vector.into_iter().collect())
    } else {
        WcharString::Raw(raw_vector)
    }
}

/// Convert the CFFI `HidDeviceInfo` struct to a native `HidDeviceInfo` struct
pub unsafe fn conv_hid_device_info(src: *mut ffi::HidDeviceInfo) -> HidResult<DeviceInfo> {
    Ok(DeviceInfo {
        path: CStr::from_ptr((*src).path).to_owned(),
        vendor_id: (*src).vendor_id,
        product_id: (*src).product_id,
        serial_number: wchar_to_string((*src).serial_number),
        release_number: (*src).release_number,
        manufacturer_string: wchar_to_string((*src).manufacturer_string),
        product_string: wchar_to_string((*src).product_string),
        usage_page: (*src).usage_page,
        usage: (*src).usage,
        interface_number: (*src).interface_number,
        bus_type: (*src).bus_type,
    })
}

/// Object for accessing HID device
pub struct HidDevice {
    _hid_device: *mut ffi::HidDevice,
}

impl HidDevice {
    pub fn from_raw(device: *mut ffi::HidDevice) -> Self {
        Self {
            _hid_device: device,
        }
    }
}

unsafe impl Send for HidDevice {}

impl Debug for HidDevice {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("HidDevice").finish()
    }
}

impl Drop for HidDevice {
    fn drop(&mut self) {
        unsafe { ffi::hid_close(self._hid_device) }
    }
}

impl HidDevice {
    /// Check size returned by other methods, if it's equal to -1 check for
    /// error and return Error, otherwise return size as unsigned number
    fn check_size(&self, res: i32) -> HidResult<usize> {
        if res == -1 {
            match self.check_error() {
                Ok(err) => Err(err),
                Err(e) => Err(e),
            }
        } else {
            Ok(res as usize)
        }
    }
}

impl HidDeviceBackendBase for HidDevice {
    fn check_error(&self) -> HidResult<HidError> {
        Ok(HidError::HidApiError {
            message: unsafe {
                match wchar_to_string(ffi::hid_error(self._hid_device)) {
                    WcharString::String(s) => s,
                    _ => return Err(HidError::HidApiErrorEmpty),
                }
            },
        })
    }

    fn write(&self, data: &[u8]) -> HidResult<usize> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }
        let res = unsafe { ffi::hid_write(self._hid_device, data.as_ptr(), data.len() as size_t) };
        self.check_size(res)
    }

    fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
        let res = unsafe { ffi::hid_read(self._hid_device, buf.as_mut_ptr(), buf.len() as size_t) };
        self.check_size(res)
    }

    fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
        let res = unsafe {
            ffi::hid_read_timeout(
                self._hid_device,
                buf.as_mut_ptr(),
                buf.len() as size_t,
                timeout,
            )
        };
        self.check_size(res)
    }

    fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }
        let res = unsafe {
            ffi::hid_send_feature_report(self._hid_device, data.as_ptr(), data.len() as size_t)
        };
        let res = self.check_size(res)?;
        if res != data.len() {
            Err(HidError::IncompleteSendError {
                sent: res,
                all: data.len(),
            })
        } else {
            Ok(())
        }
    }

    /// Set the first byte of `buf` to the 'Report ID' of the report to be read.
    /// Upon return, the first byte will still contain the Report ID, and the
    /// report data will start in `buf[1]`.
    fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
        let res = unsafe {
            ffi::hid_get_feature_report(self._hid_device, buf.as_mut_ptr(), buf.len() as size_t)
        };
        self.check_size(res)
    }

    fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }
        let res = unsafe {
            ffi::hid_send_output_report(self._hid_device, data.as_ptr(), data.len() as size_t)
        };
        let res = self.check_size(res)?;
        if res != data.len() {
            Err(HidError::IncompleteSendError {
                sent: res,
                all: data.len(),
            })
        } else {
            Ok(())
        }
    }

    fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }
        let res = unsafe {
            ffi::hid_get_input_report(self._hid_device, data.as_mut_ptr(), data.len() as size_t)
        };
        self.check_size(res)
    }

    fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
        let res = unsafe {
            ffi::hid_set_nonblocking(self._hid_device, if blocking { 0i32 } else { 1i32 })
        };
        if res == -1 {
            Err(HidError::SetBlockingModeError {
                mode: match blocking {
                    true => "blocking",
                    false => "not blocking",
                },
            })
        } else {
            Ok(())
        }
    }

    fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
        let mut buf = [0 as wchar_t; STRING_BUF_LEN];
        let res = unsafe {
            ffi::hid_get_manufacturer_string(
                self._hid_device,
                buf.as_mut_ptr(),
                STRING_BUF_LEN as size_t,
            )
        };
        let res = self.check_size(res)?;
        unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
    }

    fn get_product_string(&self) -> HidResult<Option<String>> {
        let mut buf = [0 as wchar_t; STRING_BUF_LEN];
        let res = unsafe {
            ffi::hid_get_product_string(
                self._hid_device,
                buf.as_mut_ptr(),
                STRING_BUF_LEN as size_t,
            )
        };
        let res = self.check_size(res)?;
        unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
    }

    fn get_serial_number_string(&self) -> HidResult<Option<String>> {
        let mut buf = [0 as wchar_t; STRING_BUF_LEN];
        let res = unsafe {
            ffi::hid_get_serial_number_string(
                self._hid_device,
                buf.as_mut_ptr(),
                STRING_BUF_LEN as size_t,
            )
        };
        let res = self.check_size(res)?;
        unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
    }

    fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
        let mut buf = [0 as wchar_t; STRING_BUF_LEN];
        let res = unsafe {
            ffi::hid_get_indexed_string(
                self._hid_device,
                index as c_int,
                buf.as_mut_ptr(),
                STRING_BUF_LEN,
            )
        };
        let res = self.check_size(res)?;
        unsafe { Ok(wchar_to_string(buf[..res].as_ptr()).into()) }
    }

    fn get_device_info(&self) -> HidResult<DeviceInfo> {
        let raw_device = unsafe { ffi::hid_get_device_info(self._hid_device) };
        if raw_device.is_null() {
            match self.check_error() {
                Ok(err) | Err(err) => return Err(err),
            }
        }

        unsafe { conv_hid_device_info(raw_device) }
    }

    fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
        let res = unsafe {
            ffi::hid_get_report_descriptor(self._hid_device, buf.as_mut_ptr(), buf.len())
        };
        self.check_size(res)
    }
}


================================================
FILE: src/lib.rs
================================================
// **************************************************************************
// Copyright (c) 2015 Osspial All Rights Reserved.
//
// This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
// *************************************************************************

//! This crate provides a rust abstraction over the features of the C library
//! hidapi by [signal11](https://github.com/libusb/hidapi).
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/hidapi) and can be
//! used by adding `hidapi` to the dependencies in your project's `Cargo.toml`.
//!
//! # Example
//!
//! ```rust,no_run
//! extern crate hidapi;
//!
//! use hidapi::HidApi;
//!
//! fn main() {
//!     println!("Printing all available hid devices:");
//!
//!     match HidApi::new() {
//!         Ok(api) => {
//!             for device in api.device_list() {
//!                 println!("{:04x}:{:04x}", device.vendor_id(), device.product_id());
//!             }
//!         },
//!         Err(e) => {
//!             eprintln!("Error: {}", e);
//!         },
//!     }
//! }
//! ```
//!
//! For more usage examples, please take a look at the `examples/` directory.
//!
//! # Feature flags
//!
//! - `linux-static-libusb`: uses statically linked `libusb` backend on Linux
//! - `linux-static-hidraw`: uses statically linked `hidraw` backend on Linux (default)
//! - `linux-shared-libusb`: uses dynamically linked `libusb` backend on Linux
//! - `linux-shared-hidraw`: uses dynamically linked `hidraw` backend on Linux
//! - `linux-native`: talks to hidraw directly without using the `hidapi` C library
//! - `illumos-static-libusb`: uses statically linked `libusb` backend on Illumos (default)
//! - `illumos-shared-libusb`: uses statically linked `hidraw` backend on Illumos
//! - `macos-shared-device`: enables shared access to HID devices on MacOS
//! - `windows-native`: talks to hid.dll directly without using the `hidapi` C library
//!
//! ## Linux backends
//!
//! On linux the libusb backends do not support [`DeviceInfo::usage()`] and [`DeviceInfo::usage_page()`].
//! The hidraw backend has support for them, but it might be buggy in older kernel versions.
//!
//! ## MacOS Shared device access
//!
//! Since `hidapi` 0.12 it is possible to open MacOS devices with shared access, so that multiple
//! [`HidDevice`] handles can access the same physical device. For backward compatibility this is
//! an opt-in that can be enabled with the `macos-shared-device` feature flag.
#![cfg_attr(docsrs, feature(doc_cfg))]

mod error;
mod ffi;

use cfg_if::cfg_if;
use libc::wchar_t;
use std::ffi::CStr;
use std::ffi::CString;
use std::fmt;
use std::fmt::Debug;
use std::sync::Mutex;

pub use error::HidError;

cfg_if! {
    if #[cfg(all(feature = "linux-native", target_os = "linux"))] {
        //#[cfg_attr(docsrs, doc(cfg(all(feature = "linux-native", target_os = "linux"))))]
        mod linux_native;
        use linux_native::HidApiBackend;
    } else if #[cfg(all(feature = "windows-native", target_os = "windows"))] {
        //#[cfg_attr(docsrs, doc(cfg(all(feature = "windows-native", target_os = "windows"))))]
        mod windows_native;
        use windows_native::HidApiBackend;
    } else if #[cfg(hidapi)] {
        mod hidapi;
        use hidapi::HidApiBackend;
    } else {
        compile_error!("No backend selected");
    }
}

// Automatically implement the top trait
cfg_if! {
    if #[cfg(target_os = "windows")] {
        #[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
        mod windows;
        use windows::GUID;
        /// A trait with the extra methods that are available on Windows
        trait HidDeviceBackendWindows {
            /// Get the container ID for a HID device
            fn get_container_id(&self) -> HidResult<GUID>;
        }
        trait HidDeviceBackend: HidDeviceBackendBase + HidDeviceBackendWindows + Send {}
        impl<T> HidDeviceBackend for T where T: HidDeviceBackendBase + HidDeviceBackendWindows + Send {}
    } else if #[cfg(target_os = "macos")] {
        #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
        mod macos;
        /// A trait with the extra methods that are available on macOS
        trait HidDeviceBackendMacos {
            /// Get the location ID for a [`HidDevice`] device.
            fn get_location_id(&self) -> HidResult<u32>;

            /// Check if the device was opened in exclusive mode.
            fn is_open_exclusive(&self) -> HidResult<bool>;
        }
        trait HidDeviceBackend: HidDeviceBackendBase + HidDeviceBackendMacos + Send {}
        impl<T> HidDeviceBackend for T where T: HidDeviceBackendBase + HidDeviceBackendMacos + Send {}
    } else {
        trait HidDeviceBackend: HidDeviceBackendBase + Send {}
        impl<T> HidDeviceBackend for T where T: HidDeviceBackendBase + Send {}
    }
}

pub type HidResult<T> = Result<T, HidError>;
pub const MAX_REPORT_DESCRIPTOR_SIZE: usize = 4096;

struct ContextState {
    device_discovery: bool,
    init_state: InitState,
}

enum InitState {
    NotInit,
    Init,
}

/// Global state to coordinate backing C library global context management.
static CONTEXT_STATE: Mutex<ContextState> = Mutex::new(ContextState {
    device_discovery: true,
    init_state: InitState::NotInit,
});

/// `hidapi` context.
///
/// The `hidapi` C library is lazily initialized when creating the first instance,
/// and never deinitialized. Therefore, it is allowed to create multiple `HidApi`
/// instances.
///
/// Each instance has its own device list cache.
pub struct HidApi {
    device_list: Vec<DeviceInfo>,
}

impl HidApi {
    /// Create a new hidapi context.
    ///
    /// Will also initialize the currently available device list if device discovery has not already
    /// been [disabled](Self::disable_device_discovery).
    pub fn new() -> HidResult<Self> {
        let mut state = CONTEXT_STATE.lock().unwrap();

        if let InitState::NotInit = state.init_state {
            #[cfg(all(libusb, not(target_os = "freebsd")))]
            if !state.device_discovery {
                // Do not scan for devices in libusb_init()
                // Must be set before calling it.
                // This is needed on Android, where access to USB devices is limited
                unsafe { ffi::libusb_set_option(std::ptr::null_mut(), 2) }
            }

            // Initialize the HID
            #[cfg(hidapi)]
            if unsafe { ffi::hid_init() } == -1 {
                return Err(HidError::InitializationError);
            }

            #[cfg(all(target_os = "macos", feature = "macos-shared-device"))]
            unsafe {
                ffi::macos::hid_darwin_set_open_exclusive(0)
            }

            state.init_state = InitState::Init;
        }

        let mut api = HidApi {
            device_list: Vec::with_capacity(8),
        };
        api.add_devices(0, 0)?;
        Ok(api)
    }

    /// Disable device discovery on context creation.
    ///
    /// This may be necessary on Android, where access to USB device enumeration is limited.
    ///
    /// # Panics
    ///
    /// Panics if an hidapi context has already been initialized with device discovery.
    ///
    /// <section class="warning">
    ///
    /// Avoid using this in library code, as it is an inherently global operation.
    ///
    /// This function is intended to be called by code that knows the environment it is running in.
    /// Usually this means application code either directly, or through another abstraction.
    ///
    /// </section>
    pub fn disable_device_discovery() {
        let mut state = CONTEXT_STATE.lock().unwrap();

        if let InitState::NotInit = state.init_state {
            state.device_discovery = false; // Only disable device discovery before init.
        } else if state.device_discovery {
            core::mem::drop(state); // Make sure we don't poison the lock when panicking.
            panic!("Cannot disable device discovery after HidApi has been initialized");
        }
    }

    /// Create a new hidapi context, after disabling discovery. Please avoid using this function in
    /// library code, because it forces all instances of HidApi to disable device discovery.
    ///
    /// See [`HidApi::disable_device_discovery()`].
    ///
    /// # Panics
    ///
    /// Panics if an hidapi context has already been initialized with device discovery.
    #[deprecated(
        note = "Please use only `HidApi::new()` in library code. Application code should disable device discovery explicitly."
    )]
    pub fn new_without_enumerate() -> HidResult<Self> {
        Self::disable_device_discovery();
        Self::new()
    }

    /// Refresh devices list and information about them (to access them use
    /// `device_list()` method)
    /// Identical to `reset_devices()` followed by `add_devices(0, 0)`.
    pub fn refresh_devices(&mut self) -> HidResult<()> {
        self.reset_devices()?;
        self.add_devices(0, 0)?;
        Ok(())
    }

    /// Reset devices list. Intended to be used with the `add_devices` method.
    pub fn reset_devices(&mut self) -> HidResult<()> {
        self.device_list.clear();
        Ok(())
    }

    /// Indexes devices that match the given VID and PID filters.
    /// 0 indicates no filter.
    pub fn add_devices(&mut self, vid: u16, pid: u16) -> HidResult<()> {
        self.device_list
            .append(&mut HidApiBackend::get_hid_device_info_vector(vid, pid)?);
        Ok(())
    }

    /// Returns iterator containing information about attached HID devices
    /// that have been indexed, either by `refresh_devices` or `add_devices`.
    pub fn device_list(&self) -> impl Iterator<Item = &DeviceInfo> {
        self.device_list.iter()
    }

    /// Open a HID device using a Vendor ID (VID) and Product ID (PID).
    ///
    /// When multiple devices with the same vid and pid are available, then the
    /// first one found in the internal device list will be used. There are however
    /// no guarantees, which device this will be.
    pub fn open(&self, vid: u16, pid: u16) -> HidResult<HidDevice> {
        let dev = HidApiBackend::open(vid, pid)?;
        Ok(HidDevice::from_backend(Box::new(dev)))
    }

    /// Open a HID device using a Vendor ID (VID), Product ID (PID) and
    /// a serial number.
    pub fn open_serial(&self, vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
        let dev = HidApiBackend::open_serial(vid, pid, sn)?;
        Ok(HidDevice::from_backend(Box::new(dev)))
    }

    /// The path name be determined by inspecting the device list available with [`HidApi::device_list`].
    ///
    /// Alternatively a platform-specific path name can be used (eg: /dev/hidraw0 on Linux).
    pub fn open_path(&self, device_path: &CStr) -> HidResult<HidDevice> {
        let dev = HidApiBackend::open_path(device_path)?;
        Ok(HidDevice::from_backend(Box::new(dev)))
    }

    /// Open a HID device using libusb_wrap_sys_device.
    #[cfg(libusb)]
    pub fn wrap_sys_device(&self, sys_dev: isize, interface_num: i32) -> HidResult<HidDevice> {
        let device = unsafe { ffi::hid_libusb_wrap_sys_device(sys_dev, interface_num) };

        if device.is_null() {
            match HidApiBackend::check_error() {
                Ok(err) => Err(err),
                Err(e) => Err(e),
            }
        } else {
            let dev = hidapi::HidDevice::from_raw(device);
            Ok(HidDevice::from_backend(Box::new(dev)))
        }
    }

    /// Get the last non-device specific error, which happened in the underlying hidapi C library.
    /// To get the last device specific error, use [`HidDevice::check_error`].
    ///
    /// The `Ok()` variant of the result will contain a [HidError::HidApiError](enum.HidError.html).
    ///
    /// When `Err()` is returned, then acquiring the error string from the hidapi C
    /// library failed. The contained [HidError](enum.HidError.html) is the cause, why no error could
    /// be fetched.
    #[cfg(hidapi)]
    #[deprecated(since = "2.2.3", note = "use the return values from the other methods")]
    pub fn check_error(&self) -> HidResult<HidError> {
        HidApiBackend::check_error()
    }
}

#[allow(dead_code)]
#[derive(Clone, PartialEq)]
enum WcharString {
    String(String),
    #[cfg_attr(all(feature = "linux-native", target_os = "linux"), allow(dead_code))]
    Raw(Vec<wchar_t>),
    None,
}

impl From<WcharString> for Option<String> {
    fn from(val: WcharString) -> Self {
        match val {
            WcharString::String(s) => Some(s),
            _ => None,
        }
    }
}

/// The underlying HID bus type.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub enum BusType {
    Unknown = 0x00,
    Usb = 0x01,
    Bluetooth = 0x02,
    I2c = 0x03,
    Spi = 0x04,
}

/// Device information. Use accessors to extract information about Hid devices.
///
/// Note: Methods like `serial_number()` may return None, if the conversion to a
/// String failed internally. You can however access the raw hid representation of the
/// string by calling `serial_number_raw()`
#[derive(Clone)]
pub struct DeviceInfo {
    path: CString,
    vendor_id: u16,
    product_id: u16,
    serial_number: WcharString,
    release_number: u16,
    manufacturer_string: WcharString,
    product_string: WcharString,
    #[allow(dead_code)]
    usage_page: u16,
    #[allow(dead_code)]
    usage: u16,
    interface_number: i32,
    bus_type: BusType,
}

impl DeviceInfo {
    pub fn path(&self) -> &CStr {
        &self.path
    }

    pub fn vendor_id(&self) -> u16 {
        self.vendor_id
    }

    pub fn product_id(&self) -> u16 {
        self.product_id
    }

    /// Try to call `serial_number_raw()`, if None is returned.
    pub fn serial_number(&self) -> Option<&str> {
        match self.serial_number {
            WcharString::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn serial_number_raw(&self) -> Option<&[wchar_t]> {
        match self.serial_number {
            WcharString::Raw(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn release_number(&self) -> u16 {
        self.release_number
    }

    /// Try to call `manufacturer_string_raw()`, if None is returned.
    pub fn manufacturer_string(&self) -> Option<&str> {
        match self.manufacturer_string {
            WcharString::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn manufacturer_string_raw(&self) -> Option<&[wchar_t]> {
        match self.manufacturer_string {
            WcharString::Raw(ref s) => Some(s),
            _ => None,
        }
    }

    /// Try to call `product_string_raw()`, if None is returned.
    pub fn product_string(&self) -> Option<&str> {
        match self.product_string {
            WcharString::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn product_string_raw(&self) -> Option<&[wchar_t]> {
        match self.product_string {
            WcharString::Raw(ref s) => Some(s),
            _ => None,
        }
    }

    /// Usage page is not available on linux libusb backends
    #[cfg(not(all(libusb, target_os = "linux")))]
    pub fn usage_page(&self) -> u16 {
        self.usage_page
    }

    /// Usage is not available on linux libusb backends
    #[cfg(not(all(libusb, target_os = "linux")))]
    pub fn usage(&self) -> u16 {
        self.usage
    }

    pub fn interface_number(&self) -> i32 {
        self.interface_number
    }

    pub fn bus_type(&self) -> BusType {
        self.bus_type
    }

    /// Use the information contained in `DeviceInfo` to open
    /// and return a handle to a [HidDevice](struct.HidDevice.html).
    ///
    /// By default the device path is used to open the device.
    /// When no path is available, then vid, pid and serial number are used.
    /// If both path and serial number are not available, then this function will
    /// fail with [HidError::OpenHidDeviceWithDeviceInfoError](enum.HidError.html#variant.OpenHidDeviceWithDeviceInfoError).
    ///
    /// Note, that opening a device could still be done using [HidApi::open()](struct.HidApi.html#method.open) directly.
    pub fn open_device(&self, hidapi: &HidApi) -> HidResult<HidDevice> {
        if !self.path.as_bytes().is_empty() {
            hidapi.open_path(self.path.as_c_str())
        } else if let Some(sn) = self.serial_number() {
            hidapi.open_serial(self.vendor_id, self.product_id, sn)
        } else {
            Err(HidError::OpenHidDeviceWithDeviceInfoError {
                device_info: Box::new(self.clone()),
            })
        }
    }
}

impl fmt::Debug for DeviceInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("HidDeviceInfo")
            .field("vendor_id", &self.vendor_id)
            .field("product_id", &self.product_id)
            .finish()
    }
}

/// Trait which the different backends must implement
trait HidDeviceBackendBase {
    #[cfg(hidapi)]
    fn check_error(&self) -> HidResult<HidError>;
    fn write(&self, data: &[u8]) -> HidResult<usize>;
    fn read(&self, buf: &mut [u8]) -> HidResult<usize>;
    fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize>;
    fn send_feature_report(&self, data: &[u8]) -> HidResult<()>;
    fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize>;
    fn send_output_report(&self, data: &[u8]) -> HidResult<()>;
    #[cfg(any(hidapi, target_os = "linux"))]
    fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize>;
    fn set_blocking_mode(&self, blocking: bool) -> HidResult<()>;
    fn get_device_info(&self) -> HidResult<DeviceInfo>;
    fn get_manufacturer_string(&self) -> HidResult<Option<String>>;
    fn get_product_string(&self) -> HidResult<Option<String>>;
    fn get_serial_number_string(&self) -> HidResult<Option<String>>;
    fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize>;

    fn get_indexed_string(&self, _index: i32) -> HidResult<Option<String>> {
        Err(HidError::HidApiError {
            message: "get_indexed_string: not supported".to_string(),
        })
    }
}

pub struct HidDevice {
    inner: Box<dyn HidDeviceBackend>,
}

impl Debug for HidDevice {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("HidDevice").finish_non_exhaustive()
    }
}

impl HidDevice {
    fn from_backend(inner: Box<dyn HidDeviceBackend>) -> Self {
        Self { inner }
    }
}

// Methods that use the backend
impl HidDevice {
    /// Get the last error, which happened in the underlying hidapi C library.
    ///
    /// The `Ok()` variant of the result will contain a [HidError::HidApiError](enum.HidError.html).
    ///
    /// When `Err()` is returned, then acquiring the error string from the hidapi C
    /// library failed. The contained [HidError](enum.HidError.html) is the cause, why no error could
    /// be fetched.
    #[cfg(hidapi)]
    #[deprecated(since = "2.2.3", note = "use the return values from the other methods")]
    pub fn check_error(&self) -> HidResult<HidError> {
        self.inner.check_error()
    }

    /// Write an Output report to a HID device.
    ///
    /// The first byte of `data` must contain the Report ID. For
    /// devices which only support a single report, this must be set
    /// to 0x0. The remaining bytes contain the report data. Since
    /// the Report ID is mandatory, calls to `write()` will always
    /// contain one more byte than the report contains. For example,
    /// if a hid report is 16 bytes long, 17 bytes must be passed to
    /// `write()`, the Report ID (or 0x0, for devices with a
    /// single report), followed by the report data (16 bytes). In
    /// this example, the length passed in would be 17.
    /// `write()` will send the data on the first OUT endpoint, if
    /// one exists. If it does not, it will send the data through
    /// the Control Endpoint (Endpoint 0).
    ///
    /// If successful, returns the actual number of bytes written.
    pub fn write(&self, data: &[u8]) -> HidResult<usize> {
        self.inner.write(data)
    }

    /// Read an Input report from a HID device.
    ///
    /// Input reports are returned to the host through the 'INTERRUPT IN'
    /// endpoint. The first byte will contain the Report number if the device
    /// uses numbered reports.
    ///
    /// If successful, returns the actual number of bytes read.
    pub fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
        self.inner.read(buf)
    }

    /// Read an Input report from a HID device with timeout.
    ///
    /// Input reports are returned to the host through the 'INTERRUPT IN'
    /// endpoint. The first byte will contain the Report number if the device
    /// uses numbered reports. Timeout measured in milliseconds, set -1 for
    /// blocking wait.
    ///
    /// If successful, returns the actual number of bytes read.
    pub fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
        self.inner.read_timeout(buf, timeout)
    }

    /// Send a Feature report to the device.
    ///
    /// Feature reports are sent over the Control endpoint as a
    /// Set_Report transfer.  The first byte of `data` must contain the
    /// 'Report ID'. For devices which only support a single report, this must
    /// be set to 0x0. The remaining bytes contain the report data. Since the
    /// 'Report ID' is mandatory, calls to `send_feature_report()` will always
    /// contain one more byte than the report contains. For example, if a hid
    /// report is 16 bytes long, 17 bytes must be passed to
    /// `send_feature_report()`: 'the Report ID' (or 0x0, for devices which
    /// do not use numbered reports), followed by the report data (16 bytes).
    /// In this example, the length passed in would be 17.
    pub fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
        self.inner.send_feature_report(data)
    }

    /// Get a feature report from a HID device.
    ///
    /// Set the first byte of `buf` to the 'Report ID' of the report to be read.
    /// Upon return, the first byte will still contain the Report ID, and the
    /// report data will start in `buf[1]`.
    ///
    /// If successful, returns the number of bytes read plus one for the report ID (which is still
    /// in the first byte).
    pub fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
        self.inner.get_feature_report(buf)
    }

    /// Send a Output report to the device.
    ///
    /// Output reports are sent over the Control endpoint as a Set_Report
    /// transfer. The first byte of data[] must contain the Report ID.
    /// For devices which only support a single report, this must be set
    /// to 0x0. The remaining bytes contain the report data. Since the
    /// Report ID is mandatory, calls to hid_send_output_report() will
    /// always contain one more byte than the report contains. For example,
    ///  if a hid report is 16 bytes long, 17 bytes must be passed to
    ///  hid_send_output_report(): the Report ID (or 0x0, for devices
    /// which do not use numbered reports), followed by the report
    /// data (16 bytes). In this example, the length passed in
    /// would be 17.
    pub fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
        self.inner.send_output_report(data)
    }

    /// Get a input report from a HID device
    ///
    /// Set the first byte of data to the report id of the report to be read.
    /// Set the first byte to zero if your device does not use numbered reports.
    /// After calling the function, the first byte will still contain the same report id.
    ///
    /// If successful, returns the number of bytes read plus one for the report ID (which is still
    /// in the first byte).
    #[cfg(any(hidapi, target_os = "linux"))]
    pub fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
        self.inner.get_input_report(data)
    }

    /// Set the device handle to be in blocking or in non-blocking mode. In
    /// non-blocking mode calls to `read()` will return immediately with an empty
    /// slice if there is no data to be read. In blocking mode, `read()` will
    /// wait (block) until there is data to read before returning.
    /// Modes can be changed at any time.
    pub fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
        self.inner.set_blocking_mode(blocking)
    }

    /// Get The Manufacturer String from a HID device.
    pub fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
        self.inner.get_manufacturer_string()
    }

    /// Get The Manufacturer String from a HID device.
    pub fn get_product_string(&self) -> HidResult<Option<String>> {
        self.inner.get_product_string()
    }

    /// Get The Serial Number String from a HID device.
    pub fn get_serial_number_string(&self) -> HidResult<Option<String>> {
        self.inner.get_serial_number_string()
    }

    /// Get a string from a HID device, based on its string index.
    pub fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
        self.inner.get_indexed_string(index)
    }

    /// Get a report descriptor from a HID device
    ///
    /// User has to provide a preallocated buffer where the descriptor will be copied to.
    /// It is recommended to use a preallocated buffer of [`MAX_REPORT_DESCRIPTOR_SIZE`] size.
    ///
    /// On success returns the number of bytes actually filled into `buf`
    pub fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
        self.inner.get_report_descriptor(buf)
    }

    /// Get [`DeviceInfo`] from a HID device.
    pub fn get_device_info(&self) -> HidResult<DeviceInfo> {
        self.inner.get_device_info()
    }
}


================================================
FILE: src/linux_native/ioctl.rs
================================================
//! The IOCTL calls we need for the native linux backend

use nix::{ioctl_read, ioctl_read_buf, ioctl_write_buf};

// From linux/hidraw.h
const HIDRAW_IOC_MAGIC: u8 = b'H';
const HIDRAW_IOC_GRDESCSIZE: u8 = 0x01;
const HIDRAW_SET_FEATURE: u8 = 0x06;
const HIDRAW_GET_FEATURE: u8 = 0x07;
const HIDRAW_SET_OUTPUT: u8 = 0x0b;
const HIDRAW_GET_INPUT: u8 = 0x0a;

ioctl_read!(
    hidraw_ioc_grdescsize,
    HIDRAW_IOC_MAGIC,
    HIDRAW_IOC_GRDESCSIZE,
    libc::c_int
);

ioctl_write_buf!(
    hidraw_ioc_set_feature,
    HIDRAW_IOC_MAGIC,
    HIDRAW_SET_FEATURE,
    u8
);
ioctl_read_buf!(
    hidraw_ioc_get_feature,
    HIDRAW_IOC_MAGIC,
    HIDRAW_GET_FEATURE,
    u8
);
ioctl_write_buf!(
    hidraw_ioc_set_output,
    HIDRAW_IOC_MAGIC,
    HIDRAW_SET_OUTPUT,
    u8
);
ioctl_read_buf!(hidraw_ioc_get_input, HIDRAW_IOC_MAGIC, HIDRAW_GET_INPUT, u8);


================================================
FILE: src/linux_native.rs
================================================
//! This backend uses libudev to discover devices and then talks to hidraw directly

mod ioctl;

#[cfg(feature = "linux-native-basic-udev")]
use basic_udev as udev;

use std::{
    cell::{Cell, Ref, RefCell},
    ffi::{CStr, CString, OsStr, OsString},
    fs::{File, OpenOptions},
    io::{Cursor, Read, Seek, SeekFrom},
    os::{
        fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd},
        unix::{ffi::OsStringExt, fs::OpenOptionsExt},
    },
    path::{Path, PathBuf},
};

use libc::wchar_t;
use nix::{
    errno::Errno,
    poll::{poll, PollFd, PollFlags, PollTimeout},
    sys::stat::{fstat, major, minor},
    unistd::{read, write},
};

use super::{BusType, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};
use ioctl::{
    hidraw_ioc_get_feature, hidraw_ioc_get_input, hidraw_ioc_grdescsize, hidraw_ioc_set_feature,
    hidraw_ioc_set_output,
};

// Bus values from linux/input.h
const BUS_USB: u16 = 0x03;
const BUS_BLUETOOTH: u16 = 0x05;
const BUS_I2C: u16 = 0x18;
const BUS_SPI: u16 = 0x1C;

pub struct HidApiBackend;

impl HidApiBackend {
    pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
        // The C version assumes these can't fail, and they should only fail in case
        // of memory allocation issues, at which point maybe we should panic
        let mut enumerator = match udev::Enumerator::new() {
            Ok(e) => e,
            Err(_) => return Ok(Vec::new()),
        };
        enumerator.match_subsystem("hidraw").unwrap();
        let scan = match enumerator.scan_devices() {
            Ok(s) => s,
            Err(_) => return Ok(Vec::new()),
        };

        let devices = scan
            .filter_map(|device| device_to_hid_device_info(&device))
            .flatten()
            .filter(|device| vid == 0 || device.vendor_id == vid)
            .filter(|device| pid == 0 || device.product_id == pid)
            .collect::<Vec<_>>();

        Ok(devices)
    }

    pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
        HidDevice::open(vid, pid, None)
    }

    pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
        HidDevice::open(vid, pid, Some(sn))
    }

    pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
        HidDevice::open_path(device_path)
    }
}

fn device_to_hid_device_info(raw_device: &udev::Device) -> Option<Vec<DeviceInfo>> {
    let mut infos = Vec::new();

    // We're given the hidraw device, but we actually want to go and check out
    // the info for the parent hid device.
    let device = match raw_device.parent_with_subsystem("hid") {
        Ok(Some(dev)) => dev,
        _ => return None,
    };

    let (bus, vid, pid) = device
        .property_value("HID_ID")
        .and_then(|s| s.to_str())
        .and_then(parse_hid_vid_pid)?;
    let bus_type = match bus {
        BUS_USB => BusType::Usb,
        BUS_BLUETOOTH => BusType::Bluetooth,
        BUS_I2C => BusType::I2c,
        BUS_SPI => BusType::Spi,
        _ => return None,
    };
    let name = device.property_value("HID_NAME")?;
    let serial = device.property_value("HID_UNIQ")?;
    let path = match raw_device
        .devnode()
        .map(|p| p.as_os_str().to_os_string().into_vec())
        .map(CString::new)
    {
        Some(Ok(s)) => s,
        None | Some(Err(_)) => return None,
    };

    // Thus far we've gathered all the common attributes.
    let info = DeviceInfo {
        path,
        vendor_id: vid,
        product_id: pid,
        serial_number: osstring_to_string(serial.into()),
        release_number: 0,
        manufacturer_string: WcharString::None,
        product_string: WcharString::None,
        usage_page: 0,
        usage: 0,
        interface_number: -1,
        bus_type,
    };

    // USB has a bunch more information but everything else gets the same empty
    // manufacturer and the product we read from the property above.
    let info = match bus_type {
        BusType::Usb => fill_in_usb(raw_device, info, name),
        _ => DeviceInfo {
            manufacturer_string: WcharString::String("".into()),
            product_string: osstring_to_string(name.into()),
            ..info
        },
    };

    if let Ok(descriptor) = HidrawReportDescriptor::from_syspath(raw_device.syspath()) {
        let mut usages = descriptor.usages();

        // Get the first usage page and usage for our current DeviceInfo
        if let Some((usage_page, usage)) = usages.next() {
            infos.push(DeviceInfo {
                usage_page,
                usage,
                ..info
            });

            // Now we can create DeviceInfo for all the other usages
            for (usage_page, usage) in usages {
                let prev = infos.last().unwrap();

                infos.push(DeviceInfo {
                    usage_page,
                    usage,
                    ..prev.clone()
                })
            }
        }
    } else {
        infos.push(info);
    }

    Some(infos)
}

/// Fill in the extra information that's available for a USB device.
fn fill_in_usb(device: &udev::Device, info: DeviceInfo, name: &OsStr) -> DeviceInfo {
    let usb_dev = match device.parent_with_subsystem_devtype("usb", "usb_device") {
        Ok(Some(dev)) => dev,
        Ok(None) | Err(_) => {
            return DeviceInfo {
                manufacturer_string: WcharString::String("".into()),
                product_string: osstring_to_string(name.into()),
                ..info
            }
        }
    };
    let manufacturer_string = attribute_as_wchar(&usb_dev, "manufacturer");
    let product_string = attribute_as_wchar(&usb_dev, "product");
    let release_number = attribute_as_u16(&usb_dev, "bcdDevice").unwrap_or(0);
    let interface_number = device
        .parent_with_subsystem_devtype("usb", "usb_interface")
        .ok()
        .flatten()
        .and_then(|ref dev| attribute_as_i32(dev, "bInterfaceNumber"))
        .unwrap_or(-1);

    DeviceInfo {
        release_number,
        manufacturer_string,
        product_string,
        interface_number,
        ..info
    }
}

#[derive(Default)]
struct HidrawReportDescriptor(Vec<u8>);

impl HidrawReportDescriptor {
    /// Open and parse given the "base" sysfs of the device
    pub fn from_syspath(syspath: &Path) -> HidResult<Self> {
        let path = syspath.join("device/report_descriptor");
        let mut f = File::open(path)?;
        let mut buf = Vec::new();
        f.read_to_end(&mut buf)?;

        Ok(HidrawReportDescriptor(buf))
    }

    /// Create a descriptor from a slice
    ///
    /// It returns an error if the value slice is too large for it to be a HID
    /// descriptor
    #[cfg_attr(not(test), allow(dead_code))]
    pub fn from_slice(value: &[u8]) -> HidResult<Self> {
        Ok(HidrawReportDescriptor(value.to_vec()))
    }

    pub fn usages(&self) -> impl Iterator<Item = (u16, u16)> + '_ {
        UsageIterator {
            usage_page: 0,
            cursor: Cursor::new(&self.0),
        }
    }
}

/// Iterates over the values in a HidrawReportDescriptor
struct UsageIterator<'a> {
    usage_page: u16,
    cursor: Cursor<&'a Vec<u8>>,
}

impl<'a> Iterator for UsageIterator<'a> {
    type Item = (u16, u16);

    fn next(&mut self) -> Option<Self::Item> {
        let (usage_page, page) = next_hid_usage(&mut self.cursor, self.usage_page)?;

        self.usage_page = usage_page;
        Some((usage_page, page))
    }
}

// This comes from hidapi which apparently comes from Apple's implementation of
// this
fn next_hid_usage(cursor: &mut Cursor<&Vec<u8>>, mut usage_page: u16) -> Option<(u16, u16)> {
    let mut usage = None;
    let mut usage_pair = None;
    let initial = cursor.position() == 0;

    while let Some(Ok(key)) = cursor.bytes().next() {
        // The amount to skip is calculated based off of the start of the
        // iteration so we need to keep track of that.
        let position = cursor.position() - 1;
        let key_cmd = key & 0xfc;

        let (data_len, key_size) = hid_item_size(key, cursor)?;

        match key_cmd {
            // Usage Page 6.2.2.7 (Global)
            0x4 => {
                usage_page = match hid_report_bytes(cursor, data_len) {
                    Ok(v) => v as u16,
                    Err(_) => break,
                }
            }
            // Usage 6.2.2.8 (Local)
            0x8 => {
                usage = match hid_report_bytes(cursor, data_len) {
                    Ok(v) => Some(v as u16),
                    Err(_) => break,
                }
            }
            // Collection 6.2.2.4 (Main)
            0xa0 => {
                // Usage is a Local Item, unset it
                if let Some(u) = usage.take() {
                    usage_pair = Some((usage_page, u))
                }
            }
            // Input 6.2.2.4 (Main)
		        0x80 |
            // Output 6.2.2.4 (Main)
		        0x90 |
            // Feature 6.2.2.4 (Main)
		        0xb0 |
            // End Collection 6.2.2.4 (Main)
	    0xc0  =>  {
		// Usage is a Local Item, unset it
                usage.take();
            }
            _ => {}
        }

        if cursor
            .seek(SeekFrom::Start(position + (data_len + key_size) as u64))
            .is_err()
        {
            return None;
        }

        if let Some((usage_page, usage)) = usage_pair {
            return Some((usage_page, usage));
        }
    }

    if let (true, Some(usage)) = (initial, usage) {
        return Some((usage_page, usage));
    }

    None
}

/// Gets the size of the HID item at the given position
///
/// Returns data_len and key_size when successful
fn hid_item_size(key: u8, cursor: &mut Cursor<&Vec<u8>>) -> Option<(usize, usize)> {
    // Long Item. Next byte contains the length of the data section.
    if (key & 0xf0) == 0xf0 {
        if let Some(Ok(len)) = cursor.bytes().next() {
            return Some((len.into(), 3));
        }

        // Malformed report
        return None;
    }

    // Short Item. Bottom two bits contains the size code
    match key & 0x03 {
        v @ 0..=2 => Some((v.into(), 1)),
        3 => Some((4, 1)),
        _ => unreachable!(), // & 0x03 means this can't happen
    }
}

/// Get the bytes from a HID report descriptor
///
/// Must only be called with `num_bytes` 0, 1, 2 or 4.
fn hid_report_bytes(cursor: &mut Cursor<&Vec<u8>>, num_bytes: usize) -> HidResult<u32> {
    let mut bytes: [u8; 4] = [0; 4];
    cursor.read_exact(&mut bytes[..num_bytes])?;

    Ok(u32::from_le_bytes(bytes))
}

/// Get the attribute from the device and convert it into a [`WcharString`].
fn attribute_as_wchar(dev: &udev::Device, attr: &str) -> WcharString {
    dev.attribute_value(attr)
        .map(Into::into)
        .map(osstring_to_string)
        .unwrap_or(WcharString::None)
}

/// Get the attribute from the device and convert it into a i32
///
/// On error or if the attribute is not found, it returns None.
fn attribute_as_i32(dev: &udev::Device, attr: &str) -> Option<i32> {
    dev.attribute_value(attr)
        .and_then(OsStr::to_str)
        .and_then(|v| i32::from_str_radix(v, 16).ok())
}

/// Get the attribute from the device and convert it into a u16
///
/// On error or if the attribute is not found, it returns None.
fn attribute_as_u16(dev: &udev::Device, attr: &str) -> Option<u16> {
    dev.attribute_value(attr)
        .and_then(OsStr::to_str)
        .and_then(|v| u16::from_str_radix(v, 16).ok())
}

/// Convert a [`OsString`] into a [`WcharString`]
fn osstring_to_string(s: OsString) -> WcharString {
    match s.into_string() {
        Ok(s) => WcharString::String(s),
        Err(s) => {
            // We have an array of u8s but wchar_t is i32 so we need to convert
            // them. This comes from hidapi starting from wchar_t but we start
            // from UTF-8 or whatever the OS or device gave us. We can at least
            // try to copy as much valid Unicode as there might be in the string
            let mut buf: Vec<wchar_t> = Vec::with_capacity(s.len());
            for chunk in s.into_vec().utf8_chunks() {
                // Each chunk contains first the valid portion and then invalid portion
                for c in chunk.valid().chars() {
                    buf.push(c as wchar_t);
                }
                for c in chunk.invalid() {
                    buf.push(*c as wchar_t);
                }
            }

            WcharString::Raw(buf)
        }
    }
}

/// Parse a HID_ID string to find the bus type, the vendor and product id
///
/// These strings would be of the format
///     type vendor   product
///     0003:000005AC:00008242
fn parse_hid_vid_pid(s: &str) -> Option<(u16, u16, u16)> {
    let mut elems = s.split(':').map(|s| u16::from_str_radix(s, 16));
    let devtype = elems.next()?.ok()?;
    let vendor = elems.next()?.ok()?;
    let product = elems.next()?.ok()?;

    Some((devtype, vendor, product))
}

/// Object for accessing the HID device
pub struct HidDevice {
    blocking: Cell<bool>,
    fd: OwnedFd,
    info: RefCell<Option<DeviceInfo>>,
}

unsafe impl Send for HidDevice {}

// API for the library to call us, or for internal uses
impl HidDevice {
    pub(crate) fn open(vid: u16, pid: u16, sn: Option<&str>) -> HidResult<Self> {
        for device in HidApiBackend::get_hid_device_info_vector(0, 0)?
            .iter()
            .filter(|device| device.vendor_id == vid && device.product_id == pid)
        {
            match (sn, &device.serial_number) {
                (None, _) => return Self::open_path(&device.path),
                (Some(sn), WcharString::String(serial_number)) if sn == serial_number => {
                    return Self::open_path(&device.path)
                }
                _ => continue,
            };
        }

        Err(HidError::HidApiError {
            message: "device not found".into(),
        })
    }

    pub(crate) fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
        // Paths on Linux can be anything but devnode paths are going to be ASCII
        let path = device_path.to_str().expect("path must be utf-8");
        let fd: OwnedFd = match OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
            .open(path)
        {
            Ok(f) => f.into(),
            Err(e) => {
                return Err(HidError::HidApiError {
                    message: format!("failed to open device with path {path}: {e}"),
                });
            }
        };

        let mut size = 0_i32;
        if let Err(e) = unsafe { hidraw_ioc_grdescsize(fd.as_raw_fd(), &mut size) } {
            return Err(HidError::HidApiError {
                message: format!("ioctl(GRDESCSIZE) error for {path}, not a HIDRAW device?: {e}"),
            });
        }

        Ok(Self {
            blocking: Cell::new(true),
            fd,
            info: RefCell::new(None),
        })
    }

    fn info(&self) -> HidResult<Ref<'_, DeviceInfo>> {
        if self.info.borrow().is_none() {
            let info = self.get_device_info()?;
            self.info.replace(Some(info));
        }

        let info = self.info.borrow();
        Ok(Ref::map(info, |i: &Option<DeviceInfo>| i.as_ref().unwrap()))
    }
}

impl AsFd for HidDevice {
    fn as_fd(&self) -> BorrowedFd<'_> {
        self.fd.as_fd()
    }
}

impl HidDeviceBackendBase for HidDevice {
    fn write(&self, data: &[u8]) -> HidResult<usize> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }

        Ok(write(self.fd.as_fd(), data)?)
    }

    fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
        // If the caller asked for blocking, -1 makes us wait forever
        let timeout = if self.blocking.get() { -1 } else { 0 };
        self.read_timeout(buf, timeout)
    }

    fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
        let poll_timeout = PollTimeout::try_from(timeout).map_err(|_| HidError::HidApiError {
            message: format!("invalid timeout value {timeout}"),
        })?;

        let mut pollfds = [PollFd::new(self.fd.as_fd(), PollFlags::POLLIN)];
        let res = poll(&mut pollfds, poll_timeout)?;

        if res == 0 {
            return Ok(0);
        }

        let events = pollfds[0]
            .revents()
            .map(|e| e.intersects(PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL));

        if events.is_none() || events == Some(true) {
            return Err(HidError::HidApiError {
                message: "unexpected poll error (device disconnected)".into(),
            });
        }

        match read(self.fd.as_fd(), buf) {
            Ok(w) => Ok(w),
            Err(Errno::EAGAIN) | Err(Errno::EINPROGRESS) => Ok(0),
            Err(e) => Err(e.into()),
        }
    }

    fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
        if data.is_empty() {
            return Err(HidError::InvalidZeroSizeData);
        }

        let res = match unsafe { hidraw_ioc_set_feature(self.fd.as_raw_fd(), data) } {
            Ok(n) => n as usize,
            Err(e) => {
                return Err(HidError::HidApiError {
                    message: format!("ioctl (GFEATURE): {e}"),
                })
            }
        };

        if res != data.len() {
            return Err(HidError::IncompleteSendError {
                sent: res,
                all: data.len(),
            });
        }

        Ok(())
    }

    fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
        let res = match unsafe { hidraw_ioc_get_feature(self.fd.as_raw_fd(), buf) } {
            Ok(n) => n as usize,
            Err(e) => {
                return Err(HidError::HidApiError {
                    message: format!("ioctl (GFEATURE): {e}"),
                })
            }
        };

        Ok(res)
    }

    fn send_output_report(&self, buf: &[u8]) -> HidResult<()> {
        let res = match unsafe { hidraw_ioc_set_output(self.fd.as_raw_fd(), buf) } {
            Ok(n) => n,
            Err(e) => {
                return Err(HidError::HidApiError {
                    message: format!("ioctl (SOUTPUT): {e}"),
                });
            }
        };

        if res as usize != buf.len() {
            return Err(HidError::IncompleteSendError {
                sent: res as usize,
                all: buf.len(),
            });
        }

        Ok(())
    }

    fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
        match unsafe { hidraw_ioc_get_input(self.fd.as_raw_fd(), data) } {
            Ok(n) => Ok(n as usize),
            Err(e) => Err(HidError::HidApiError {
                message: format!("ioctl (GINPUT): {e}"),
            }),
        }
    }

    fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
        self.blocking.set(blocking);
        Ok(())
    }

    fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
        let info = self.info()?;
        Ok(info.manufacturer_string().map(str::to_string))
    }

    fn get_product_string(&self) -> HidResult<Option<String>> {
        let info = self.info()?;
        Ok(info.product_string().map(str::to_string))
    }

    fn get_serial_number_string(&self) -> HidResult<Option<String>> {
        let info = self.info()?;
        Ok(info.serial_number().map(str::to_string))
    }

    fn get_device_info(&self) -> HidResult<DeviceInfo> {
        // What we have is a descriptor to a file in /dev but we need a syspath
        // so we get the major/minor from there and generate our syspath
        let devnum = fstat(self.fd.as_fd())?.st_rdev;
        let syspath: PathBuf = format!("/sys/dev/char/{}:{}", major(devnum), minor(devnum)).into();

        // The clone is a bit silly but we can't implement Copy. Maybe it's not
        // much worse than doing the conversion to Rust from interacting with C.
        let device = udev::Device::from_syspath(&syspath)?;
        match device_to_hid_device_info(&device) {
            Some(info) => Ok(info[0].clone()),
            None => Err(HidError::HidApiError {
                message: "failed to create device info".into(),
            }),
        }
    }

    fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
        let devnum = fstat(self.fd.as_fd())?.st_rdev;
        let syspath: PathBuf = format!("/sys/dev/char/{}:{}", major(devnum), minor(devnum)).into();

        let descriptor = HidrawReportDescriptor::from_syspath(&syspath)?;
        let min_size = buf.len().min(descriptor.0.len());
        buf[..min_size].copy_from_slice(&descriptor.0[..min_size]);
        Ok(min_size)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_hid_vid_pid() {
        assert_eq!(None, parse_hid_vid_pid("Hello World"));
        assert_eq!(Some((1, 1, 1)), parse_hid_vid_pid("1:1:1"));
        assert_eq!(Some((0x11, 0x17, 0x18)), parse_hid_vid_pid("11:0017:00018"));
    }

    #[test]
    fn test_hidraw_report_descriptor_1() {
        let data = include_bytes!("../tests/assets/mouse1.data");
        let desc = HidrawReportDescriptor::from_slice(&data[..]).expect("descriptor");
        let values = desc.usages().collect::<Vec<_>>();

        assert_eq!(vec![(65468, 136)], values);
    }

    #[test]
    fn test_hidraw_report_descriptor_2() {
        let data = include_bytes!("../tests/assets/mouse2.data");
        let desc = HidrawReportDescriptor::from_slice(&data[..]).expect("descriptor");
        let values = desc.usages().collect::<Vec<_>>();

        let expected = vec![(1, 2), (1, 1), (1, 128), (12, 1), (65280, 14)];
        assert_eq!(expected, values);
    }

    #[test]
    fn invalid_string_conversion() {
        let good = OsStr::new("hello world").to_os_string();
        assert!(matches!(osstring_to_string(good), WcharString::String(s) if s == "hello world"));

        // Value found in the field that made us panic
        let bad_bytes: Vec<u8> = vec![0xD6, 0x34, 0x25, 0xB9, 0x04, 0x1c, 0xA4, 0xFD];
        let bad_chars = bad_bytes.iter().map(|c| *c as wchar_t).collect::<Vec<_>>();
        let bad = OsString::from_vec(bad_bytes);
        assert!(matches!(osstring_to_string(bad), WcharString::Raw(v) if v == bad_chars));

        // Check that we do convert Unicode chars into wchar_t rather than their
        // bytes when we deal with a partially valid Unicode string.
        let mut crab_vec = OsStr::new("🦀").to_os_string().into_encoded_bytes();
        crab_vec.push(0xD6_u8);
        let crab = unsafe { OsString::from_encoded_bytes_unchecked(crab_vec) };
        let crab_wchar: Vec<wchar_t> = vec![0x1F980, 0xD6];
        assert!(matches!(osstring_to_string(crab), WcharString::Raw(v) if v == crab_wchar));
    }
}


================================================
FILE: src/macos.rs
================================================
use libc::c_int;

use crate::ffi;
use crate::{HidApi, HidDevice, HidResult};

impl HidApi {
    /// Changes the behavior of all further calls that open a new [`HidDevice`]
    /// like [`HidApi::open`] or [`HidApi::open_path`]. By default on Darwin
    /// platform all devices opened by [`HidApi`] are opened in exclusive mode.
    ///
    /// When `exclusive` is set to:
    ///   * `false` - all further devices will be opened in non-exclusive mode.
    ///   * `true` all further devices will be opened in exclusive mode.
    pub fn set_open_exclusive(&self, exclusive: bool) {
        unsafe { ffi::macos::hid_darwin_set_open_exclusive(exclusive as c_int) }
    }

    /// Get the current opening behavior set by [`HidApi::set_open_exclusive`].
    pub fn get_open_exclusive(&self) -> bool {
        unsafe { ffi::macos::hid_darwin_get_open_exclusive() != 0 }
    }
}

impl HidDevice {
    /// Get the location ID for a [`HidDevice`] device.
    pub fn get_location_id(&self) -> HidResult<u32> {
        self.inner.get_location_id()
    }

    /// Check if the device was opened in exclusive mode.
    pub fn is_open_exclusive(&self) -> HidResult<bool> {
        self.inner.is_open_exclusive()
    }
}


================================================
FILE: src/windows.rs
================================================
use crate::{HidDevice, HidResult};
pub use windows_sys::core::GUID;

impl HidDevice {
    /// Get the container ID for a HID device.
    ///
    /// This function returns the `DEVPKEY_Device_ContainerId` property of the
    /// given device. This can be used to correlate different interfaces/ports
    /// on the same hardware device.
    pub fn get_container_id(&self) -> HidResult<GUID> {
        self.inner.get_container_id()
    }
}


================================================
FILE: src/windows_native/descriptor/encoder.rs
================================================
use crate::windows_native::descriptor::typedefs::{Caps, LinkCollectionNode};
use crate::windows_native::descriptor::types::{ItemNodeType, Items, MainItemNode, MainItems};
use crate::windows_native::error::{WinError, WinResult};
use crate::windows_native::utils::PeakIterExt;

pub fn encode_descriptor(
    main_item_list: &[MainItemNode],
    caps_list: &[Caps],
    link_collection_nodes: &[LinkCollectionNode],
) -> WinResult<Vec<u8>> {
    // ***********************************
    // Encode the report descriptor output
    // ***********************************

    let mut writer = DescriptorWriter::default();

    let mut last_report_id = 0;
    let mut last_usage_page = 0;
    let mut last_physical_min = 0; // If both, Physical Minimum and Physical Maximum are 0, the logical limits should be taken as physical limits according USB HID spec 1.11 chapter 6.2.2.7
    let mut last_physical_max = 0;
    let mut last_unit_exponent = 0; // If Unit Exponent is Undefined it should be considered as 0 according USB HID spec 1.11 chapter 6.2.2.7
    let mut last_unit = 0; // If the first nibble is 7, or second nibble of Unit is 0, the unit is None according USB HID spec 1.11 chapter 6.2.2.7
    let mut inhibit_write_of_usage = false; // Needed in case of delimited usage print, before the normal collection or cap
    let mut report_count = 0;

    for (current, next) in main_item_list.iter().peaking() {
        let rt_idx = current.main_item_type;
        let caps_idx = current.caps_index;
        match current.main_item_type {
            MainItems::Collection => {
                if last_usage_page
                    != link_collection_nodes[current.collection_index].link_usage_page
                {
                    // Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last
                    last_usage_page =
                        link_collection_nodes[current.collection_index].link_usage_page;
                    writer.write(Items::GlobalUsagePage, last_usage_page)?;
                }
                if inhibit_write_of_usage {
                    // Inhibit only once after DELIMITER statement
                    inhibit_write_of_usage = false;
                } else {
                    // Write "Usage" of collection
                    writer.write(
                        Items::LocalUsage,
                        link_collection_nodes[current.collection_index].link_usage,
                    )?;
                }
                // Write begin of "Collection"
                writer.write(
                    Items::MainCollection,
                    link_collection_nodes[current.collection_index].collection_type(),
                )?;
            }
            MainItems::CollectionEnd => {
                // Write "End Collection"
                writer.write(Items::MainCollectionEnd, 0)?;
            }
            MainItems::DelimiterOpen => {
                // Current.collection_index seems to always be != -1 -> removing branches compared to c
                // Write "Usage Page" inside of a collection delmiter section
                if last_usage_page
                    != link_collection_nodes[current.collection_index].link_usage_page
                {
                    last_usage_page =
                        link_collection_nodes[current.collection_index].link_usage_page;
                    writer.write(
                        Items::GlobalUsagePage,
                        link_collection_nodes[current.collection_index].collection_type(),
                    )?;
                }
                // Write "Delimiter Open"
                writer.write(Items::LocalDelimiter, 1)?; // 1 = open set of aliased usages
            }
            MainItems::DelimiterUsage => {
                // Current.collection_index seems to always be != -1 -> removing branches compared to c
                // Write aliased collection "Usage"
                writer.write(
                    Items::LocalUsage,
                    link_collection_nodes[current.collection_index].link_usage,
                )?;
            }
            MainItems::DelimiterClose => {
                // Write "Delimiter Close"
                writer.write(Items::LocalDelimiter, 0)?; // 0 = close set of aliased usages
                                                         // Inhibit next usage write
                inhibit_write_of_usage = true;
            }
            _ if current.node_type == ItemNodeType::Padding => {
                // Padding
                // The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
                // in the reports are filled with the same style of constant padding.

                // Write "Report Size" with number of padding bits
                writer.write(
                    Items::GlobalReportSize,
                    current.last_bit - current.first_bit + 1,
                )?;

                // Write "Report Count" for padding always as 1
                writer.write(Items::GlobalReportCount, 1)?;

                if rt_idx == MainItems::Input {
                    // Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
                    writer.write(Items::MainInput, 0x03)?; // Const / Abs
                } else if rt_idx == MainItems::Output {
                    // Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
                    writer.write(Items::MainOutput, 0x03)?; // Const / Abs
                } else if rt_idx == MainItems::Feature {
                    // Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
                    writer.write(Items::MainFeature, 0x03)?; // Const / Abs
                }
                report_count = 0;
            }
            _ if caps_list[caps_idx as usize].is_button_cap() => {
                let caps = caps_list[caps_idx as usize];
                // Button
                // (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)

                if last_report_id != caps.report_id {
                    // Write "Report ID" if changed
                    last_report_id = caps.report_id;
                    writer.write(Items::GlobalReportId, last_report_id)?;
                }

                // Write "Usage Page" when changed
                if caps.usage_page != last_usage_page {
                    last_usage_page = caps.usage_page;
                    writer.write(Items::GlobalUsagePage, last_usage_page)?;
                }

                // Write only local report items for each cap, if ReportCount > 1
                if caps.is_range() {
                    report_count += caps.range().data_index_max - caps.range().data_index_min;
                }

                if inhibit_write_of_usage {
                    // Inhibit only once after Delimiter - Reset flag
                    inhibit_write_of_usage = false;
                } else if caps.is_range() {
                    // Write range from "Usage Minimum" to "Usage Maximum"
                    writer.write(Items::LocalUsageMinimum, caps.range().usage_min)?;
                    writer.write(Items::LocalUsageMaximum, caps.range().usage_max)?;
                } else {
                    // Write single "Usage"
                    writer.write(Items::LocalUsage, caps.not_range().usage)?;
                }

                if caps.is_designator_range() {
                    // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
                    writer.write(Items::LocalDesignatorMinimum, caps.range().designator_min)?;
                    writer.write(Items::LocalDesignatorMaximum, caps.range().designator_max)?;
                } else if caps.not_range().designator_index != 0 {
                    // Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
                    // that specifies the number of additional descriptor sets.
                    // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
                    // Write single "Designator Index"
                    writer.write(
                        Items::LocalDesignatorIndex,
                        caps.not_range().designator_index,
                    )?;
                }

                if caps.is_string_range() {
                    // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
                    writer.write(Items::LocalStringMinimum, caps.range().string_min)?;
                    writer.write(Items::LocalStringMaximum, caps.range().string_max)?;
                } else if caps.not_range().string_index != 0 {
                    // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
                    // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
                    // Write single "String Index"
                    writer.write(Items::LocalString, caps.not_range().string_index)?;
                }

                if next.is_some_and(|next| {
                    next.main_item_type == rt_idx &&
                        next.node_type == ItemNodeType::Cap &&
                        !caps.is_range() && // This node in list is no array
                        !caps_list[next.caps_index as usize].is_range() && // Next node in list is no array
                        caps_list[next.caps_index as usize].is_button_cap() &&
                        caps_list[next.caps_index as usize].usage_page == caps.usage_page &&
                        caps_list[next.caps_index as usize].report_id == caps.report_id &&
                        caps_list[next.caps_index as usize].bit_field == caps.bit_field
                }) {
                    if next.unwrap().first_bit != current.first_bit {
                        // In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented

                        // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
                        report_count += 1;
                    }
                } else {
                    if caps.button().logical_min == 0 && caps.button().logical_max == 0 {
                        // While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,
                        // the preparsed data contain both fields set to zero, for the case of simple buttons
                        // Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1
                        writer.write(Items::GlobalLogicalMinimum, 0)?;
                        writer.write(Items::GlobalLogicalMaximum, 1)?;
                    } else {
                        // Write logical range from "Logical Minimum" to "Logical Maximum"
                        writer.write(Items::GlobalLogicalMinimum, caps.button().logical_min)?;
                        writer.write(Items::GlobalLogicalMaximum, caps.button().logical_max)?;
                    }

                    // Write "Report Size"
                    writer.write(Items::GlobalReportSize, caps.report_size)?;

                    // Write "Report Count"
                    if !caps.is_range() {
                        // Variable bit field with one bit per button
                        // In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented
                        writer.write(Items::GlobalReportCount, caps.report_count + report_count)?;
                    } else {
                        // Button array of "Report Size" x "Report Count
                        writer.write(Items::GlobalReportCount, caps.report_count)?;
                    }

                    // Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state
                    if last_physical_min != 0 {
                        // Write "Physical Minimum", but only if changed
                        last_physical_min = 0;
                        writer.write(Items::GlobalPhysicalMinimum, last_physical_min)?;
                    }
                    if last_physical_max != 0 {
                        // Write "Physical Maximum", but only if changed
                        last_physical_max = 0;
                        writer.write(Items::GlobalPhysicalMaximum, last_physical_max)?;
                    }
                    if last_unit_exponent != 0 {
                        // Write "Unit Exponent", but only if changed
                        last_unit_exponent = 0;
                        writer.write(Items::GlobalUnitExponent, last_unit_exponent)?;
                    }
                    if last_unit != 0 {
                        // Write "Unit",but only if changed
                        last_unit = 0;
                        writer.write(Items::GlobalUnit, last_unit)?;
                    }

                    // Write "Input" main item
                    if rt_idx == MainItems::Input {
                        writer.write(Items::MainInput, caps.bit_field)?;
                    }
                    // Write "Output" main item
                    else if rt_idx == MainItems::Output {
                        writer.write(Items::MainOutput, caps.bit_field)?;
                    }
                    // Write "Feature" main item
                    else if rt_idx == MainItems::Feature {
                        writer.write(Items::MainFeature, caps.bit_field)?;
                    }
                    report_count = 0;
                }
            }
            _ => {
                let mut caps = caps_list[caps_idx as usize];

                if last_report_id != caps.report_id {
                    // Write "Report ID" if changed
                    last_report_id = caps.report_id;
                    writer.write(Items::GlobalReportId, last_report_id)?;
                }

                // Write "Usage Page" if changed
                if caps.usage_page != last_usage_page {
                    last_usage_page = caps.usage_page;
                    writer.write(Items::GlobalUsagePage, last_usage_page)?;
                }

                if inhibit_write_of_usage {
                    // Inhibit only once after Delimiter - Reset flag
                    inhibit_write_of_usage = false;
                } else if caps.is_range() {
                    // Write usage range from "Usage Minimum" to "Usage Maximum"
                    writer.write(Items::LocalUsageMinimum, caps.range().usage_min)?;
                    writer.write(Items::LocalUsageMaximum, caps.range().usage_max)?;
                } else {
                    // Write single "Usage"
                    writer.write(Items::LocalUsage, caps.not_range().usage)?;
                }

                if caps.is_designator_range() {
                    // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
                    writer.write(Items::LocalDesignatorMinimum, caps.range().designator_min)?;
                    writer.write(Items::LocalDesignatorMaximum, caps.range().designator_max)?;
                } else if caps.not_range().designator_index != 0 {
                    // Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
                    // that specifies the number of additional descriptor sets.
                    // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
                    // Write single "Designator Index"
                    writer.write(
                        Items::LocalDesignatorIndex,
                        caps.not_range().designator_index,
                    )?;
                }

                if caps.is_string_range() {
                    // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
                    writer.write(Items::LocalStringMinimum, caps.range().string_min)?;
                    writer.write(Items::LocalStringMaximum, caps.range().string_max)?;
                } else if caps.not_range().string_index != 0 {
                    // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
                    // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
                    // Write single "String Index"
                    writer.write(Items::LocalString, caps.not_range().string_index)?;
                }

                if (caps.bit_field & 0x02) != 0x02 {
                    // In case of an value array overwrite "Report Count"
                    caps.report_count =
                        caps.range().data_index_max - caps.range().data_index_min + 1;
                }

                #[allow(clippy::blocks_in_if_conditions)]
                if next.is_some_and(|next| {
                    let next_caps = caps_list
                        .get(next.caps_index as usize)
                        .copied()
                        .unwrap_or_else(|| unsafe { std::mem::zeroed() });
                    next.main_item_type == rt_idx
                        && next.node_type == ItemNodeType::Cap
                        && !next_caps.is_button_cap()
                        && !caps.is_range()
                        && !next_caps.is_range()
                        && next_caps.usage_page == caps.usage_page
                        && next_caps.not_button().logical_min == caps.not_button().logical_min
                        && next_caps.not_button().logical_max == caps.not_button().logical_max
                        && next_caps.not_button().physical_min == caps.not_button().physical_min
                        && next_caps.not_button().physical_max == caps.not_button().physical_max
                        && next_caps.units_exp == caps.units_exp
                        && next_caps.units == caps.units
                        && next_caps.report_size == caps.report_size
                        && next_caps.report_id == caps.report_id
                        && next_caps.bit_field == caps.bit_field
                        && next_caps.report_count == 1
                        && caps.report_count == 1
                }) {
                    // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
                    report_count += 1;
                } else {
                    // Value

                    // Write logical range from "Logical Minimum" to "Logical Maximum"
                    writer.write(Items::GlobalLogicalMinimum, caps.not_button().logical_min)?;
                    writer.write(Items::GlobalLogicalMaximum, caps.not_button().logical_max)?;

                    if (last_physical_min != caps.not_button().physical_min)
                        || (last_physical_max != caps.not_button().physical_max)
                    {
                        // Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed
                        last_physical_min = caps.not_button().physical_min;
                        last_physical_max = caps.not_button().physical_max;
                        writer.write(Items::GlobalPhysicalMinimum, last_physical_min)?;
                        writer.write(Items::GlobalPhysicalMaximum, last_physical_max)?;
                    }

                    if last_unit_exponent != caps.units_exp {
                        // Write "Unit Exponent", but only if changed
                        last_unit_exponent = caps.units_exp;
                        writer.write(Items::GlobalUnitExponent, last_unit_exponent)?;
                    }

                    if last_unit != caps.units {
                        // Write physical "Unit", but only if changed
                        last_unit = caps.units;
                        writer.write(Items::GlobalUnit, last_unit)?;
                    }

                    // Write "Report Size"
                    writer.write(Items::GlobalReportSize, caps.report_size)?;

                    // Write "Report Count"
                    writer.write(Items::GlobalReportCount, caps.report_count + report_count)?;

                    if rt_idx == MainItems::Input {
                        // Write "Input" main item
                        writer.write(Items::MainInput, caps.bit_field)?;
                    } else if rt_idx == MainItems::Output {
                        // Write "Output" main item
                        writer.write(Items::MainOutput, caps.bit_field)?;
                    } else if rt_idx == MainItems::Feature {
                        // Write "Feature" main item
                        writer.write(Items::MainFeature, caps.bit_field)?;
                    }
                    report_count = 0;
                }
            }
        }
    }

    Ok(writer.finish())
}

#[derive(Default)]
struct DescriptorWriter(Vec<u8>);

impl DescriptorWriter {
    // Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2
    fn write(&mut self, item: Items, data: impl Into<i64>) -> WinResult<()> {
        let data = data.into();
        match item {
            Items::MainCollectionEnd => {
                self.0.push(item as u8);
            }
            Items::GlobalLogicalMinimum
            | Items::GlobalLogicalMaximum
            | Items::GlobalPhysicalMinimum
            | Items::GlobalPhysicalMaximum => {
                if let Ok(data) = i8::try_from(data) {
                    self.0.push((item as u8) + 0x01);
                    self.0.extend(data.to_le_bytes())
                } else if let Ok(data) = i16::try_from(data) {
                    self.0.push((item as u8) + 0x02);
                    self.0.extend(data.to_le_bytes())
                } else if let Ok(data) = i32::try_from(data) {
                    self.0.push((item as u8) + 0x03);
                    self.0.extend(data.to_le_bytes())
                } else {
                    return Err(WinError::InvalidPreparsedData);
                }
            }
            _ => {
                if let Ok(data) = u8::try_from(data) {
                    self.0.push((item as u8) + 0x01);
                    self.0.extend(data.to_le_bytes())
                } else if let Ok(data) = u16::try_from(data) {
                    self.0.push((item as u8) + 0x02);
                    self.0.extend(data.to_le_bytes())
                } else if let Ok(data) = u32::try_from(data) {
                    self.0.push((item as u8) + 0x03);
                    self.0.extend(data.to_le_bytes())
                } else {
                    return Err(WinError::InvalidPreparsedData);
                }
            }
        }
        Ok(())
    }

    fn finish(self) -> Vec<u8> {
        self.0
    }
}


================================================
FILE: src/windows_native/descriptor/mod.rs
================================================
mod encoder;
#[cfg(test)]
mod tests;
mod typedefs;
mod types;

use crate::windows_native::descriptor::encoder::encode_descriptor;
use crate::windows_native::descriptor::typedefs::{Caps, HidpPreparsedData, LinkCollectionNode};
use crate::windows_native::descriptor::types::{
    BitRange, ItemNodeType, MainItemNode, MainItems, ReportType,
};
use crate::windows_native::error::{WinError, WinResult};
use crate::windows_native::hid::PreparsedData;
use crate::windows_native::utils::PeakIterExt;
use std::collections::HashMap;
use std::ffi::c_void;
use std::slice;

pub fn get_descriptor(pp_data: &PreparsedData) -> WinResult<Vec<u8>> {
    unsafe { get_descriptor_ptr(pp_data.as_ptr()) }
}

unsafe fn get_descriptor_ptr(pp_data: *const c_void) -> WinResult<Vec<u8>> {
    let (header, caps_list, link_collection_nodes) = extract_structures(pp_data)?;

    let list = reconstruct_descriptor(header, caps_list, link_collection_nodes);

    encode_descriptor(&list, caps_list, link_collection_nodes)
}

unsafe fn extract_structures<'a>(
    pp_data: *const c_void,
) -> WinResult<(HidpPreparsedData, &'a [Caps], &'a [LinkCollectionNode])> {
    let header: *const HidpPreparsedData = pp_data as _;

    // Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure
    ensure!(
        &(*header).magic_key == b"HidP KDR",
        Err(WinError::InvalidPreparsedData)
    );

    let caps_ptr: *const Caps = header.offset(1) as _;
    let caps_len = ReportType::values()
        .into_iter()
        .map(|r| (*header).caps_info[r as usize].last_cap)
        .max()
        .unwrap() as usize;

    let link_ptr: *const LinkCollectionNode = ((caps_ptr as *const c_void)
        .offset((*header).first_byte_of_link_collection_array as isize))
        as _;
    let link_len = (*header).number_link_collection_nodes as usize;

    Ok((
        *header,
        slice::from_raw_parts(caps_ptr, caps_len),
        slice::from_raw_parts(link_ptr, link_len),
    ))
}

fn reconstruct_descriptor(
    header: HidpPreparsedData,
    caps_list: &[Caps],
    link_collection_nodes: &[LinkCollectionNode],
) -> Vec<MainItemNode> {
    // ****************************************************************************************************************************
    // Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)
    // coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
    // ****************************************************************************************************************************
    let mut coll_bit_range: HashMap<(usize, u8, ReportType), BitRange> = HashMap::new();
    for rt_idx in ReportType::values() {
        let caps_info = header.caps_info[rt_idx as usize];
        for caps_idx in caps_info.first_cap..caps_info.last_cap {
            let caps = caps_list[caps_idx as usize];
            let range = caps.get_bit_range();
            coll_bit_range
                .entry((caps.link_collection as usize, caps.report_id, rt_idx))
                .and_modify(|r| *r = r.merge(range))
                .or_insert(range);
        }
    }

    // *************************************************************************
    // -Determine hierachy levels of each collections and store it in:
    //  coll_levels[COLLECTION_INDEX]
    // -Determine number of direct childs of each collections and store it in:
    //  coll_number_of_direct_childs[COLLECTION_INDEX]
    // *************************************************************************
    let mut max_coll_level = 0;
    let mut coll_levels = vec![-1; link_collection_nodes.len()];
    let mut coll_number_of_direct_childs = vec![0; link_collection_nodes.len()];
    {
        let mut actual_coll_level = 0;
        let mut collection_node_idx = 0;
        while actual_coll_level >= 0 {
            coll_levels[collection_node_idx] = actual_coll_level;
            let node = link_collection_nodes[collection_node_idx];
            if node.number_of_children > 0 && coll_levels[node.first_child as usize] == -1 {
                actual_coll_level += 1;
                coll_levels[collection_node_idx] = actual_coll_level;
                max_coll_level = max_coll_level.max(actual_coll_level);
                coll_number_of_direct_childs[collection_node_idx] += 1;
                collection_node_idx = node.first_child as usize;
            } else if node.next_sibling != 0 {
                coll_number_of_direct_childs[node.parent as usize] += 1;
                collection_node_idx = node.next_sibling as usize;
            } else {
                actual_coll_level -= 1;
                if actual_coll_level >= 0 {
                    collection_node_idx = node.parent as usize;
                }
            }
        }
    }

    // *********************************************************************************
    // Propagate the bit range of each report from the child collections to their parent
    // and store the merged result for the parent
    // *********************************************************************************
    for actual_coll_level in (0..max_coll_level).rev() {
        for collection_node_idx in 0..link_collection_nodes.len() {
            if coll_levels[collection_node_idx] == actual_coll_level {
                let mut child_idx = link_collection_nodes[collection_node_idx].first_child as usize;
                while child_idx != 0 {
                    for reportid_idx in 0..=255 {
                        for rt_idx in ReportType::values() {
                            if let Some(child) = coll_bit_range
                                .get(&(child_idx, reportid_idx, rt_idx))
                                .copied()
                            {
                                coll_bit_range
                                    .entry((collection_node_idx, reportid_idx, rt_idx))
                                    .and_modify(|r| *r = r.merge(child))
                                    .or_insert(child);
                            }
                            child_idx = link_collection_nodes[child_idx].next_sibling as usize;
                        }
                    }
                }
            }
        }
    }

    // *************************************************************************************************
    // Determine child collection order of the whole hierachy, based on previously determined bit ranges
    // and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
    // *************************************************************************************************
    let mut coll_child_order: HashMap<(usize, u16), usize> = HashMap::new();
    {
        let mut coll_parsed_flag = vec![false; link_collection_nodes.len()];
        let mut actual_coll_level = 0;
        let mut collection_node_idx = 0;
        while actual_coll_level >= 0 {
            if coll_number_of_direct_childs[collection_node_idx] != 0
                && !coll_parsed_flag
                    [link_collection_nodes[collection_node_idx].first_child as usize]
            {
                coll_parsed_flag[link_collection_nodes[collection_node_idx].first_child as usize] =
                    true;

                {
                    // Create list of child collection indices
                    // sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild
                    // which seems to match teh original order, as long as no bit position needs to be considered
                    let mut child_idx =
                        link_collection_nodes[collection_node_idx].first_child as usize;
                    let mut child_count = coll_number_of_direct_childs[collection_node_idx] - 1;
                    coll_child_order.insert((collection_node_idx, child_count), child_idx);
                    while link_collection_nodes[child_idx].next_sibling != 0 {
                        child_count -= 1;
                        child_idx = link_collection_nodes[child_idx].next_sibling as usize;
                        coll_child_order.insert((collection_node_idx, child_count), child_idx);
                    }
                }

                if coll_number_of_direct_childs[collection_node_idx] > 1 {
                    // Sort child collections indices by bit positions
                    for rt_idx in ReportType::values() {
                        for report_idx in 0..=255 {
                            for child_idx in 1..coll_number_of_direct_childs[collection_node_idx] {
                                // since the coll_bit_range array is not sorted, we need to reference the collection index in
                                // our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort
                                let prev_coll_idx = *coll_child_order
                                    .get(&(collection_node_idx, child_idx - 1))
                                    .unwrap();
                                let cur_coll_idx = *coll_child_order
                                    .get(&(collection_node_idx, child_idx))
                                    .unwrap();
                                let swap = coll_bit_range
                                    .get(&(prev_coll_idx, report_idx, rt_idx))
                                    .map(|prev| prev.first_bit)
                                    .zip(
                                        coll_bit_range
                                            .get(&(cur_coll_idx, report_idx, rt_idx))
                                            .map(|prev| prev.first_bit),
                                    )
                                    .map_or(false, |(prev, cur)| prev > cur);
                                if swap {
                                    coll_child_order.insert(
                                        (collection_node_idx, (child_idx - 1)),
                                        cur_coll_idx,
                                    );
                                    coll_child_order
                                        .insert((collection_node_idx, child_idx), prev_coll_idx);
                                }
                            }
                        }
                    }
                }
                actual_coll_level += 1;
                collection_node_idx =
                    link_collection_nodes[collection_node_idx].first_child as usize;
            } else if link_collection_nodes[collection_node_idx].next_sibling != 0 {
                collection_node_idx =
                    link_collection_nodes[collection_node_idx].next_sibling as usize;
            } else {
                actual_coll_level -= 1;
                if actual_coll_level >= 0 {
                    collection_node_idx =
                        link_collection_nodes[collection_node_idx].parent as usize;
                }
            }
        }
    }

    // ***************************************************************************************
    // Create sorted main_item_list containing all the Collection and CollectionEnd main items
    // ***************************************************************************************
    let mut main_item_list: Vec<MainItemNode> = Vec::new();
    {
        let mut coll_last_written_child = vec![-1i32; link_collection_nodes.len()];

        let mut actual_coll_level = 0;
        let mut collection_node_idx = 0;
        let mut first_delimiter_node = false;

        main_item_list.push(MainItemNode::new(
            0,
            0,
            ItemNodeType::Collection,
            0,
            collection_node_idx,
            MainItems::Collection,
            0,
        ));
        while actual_coll_level >= 0 {
            if coll_number_of_direct_childs[collection_node_idx] != 0
                && coll_last_written_child[collection_node_idx] == -1
            {
                // Collection has child collections, but none is written to the list yet
                coll_last_written_child[collection_node_idx] =
                    coll_child_order[&(collection_node_idx, 0)] as i32;
                collection_node_idx = coll_child_order[&(collection_node_idx, 0)];

                // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
                // While the order in the WIN32 capabiliy strutures is the opposite:
                // Here the preferred usage is the last aliased usage in the sequence.
                if link_collection_nodes[collection_node_idx].is_alias() && !first_delimiter_node {
                    first_delimiter_node = true;
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::DelimiterUsage,
                        0,
                    ));
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::DelimiterClose,
                        0,
                    ));
                } else {
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::Collection,
                        0,
                    ));
                    actual_coll_level += 1;
                }
            } else if coll_number_of_direct_childs[collection_node_idx] > 1
                && coll_last_written_child[collection_node_idx]
                    != coll_child_order[&(
                        collection_node_idx,
                        coll_number_of_direct_childs[collection_node_idx] - 1,
                    )] as i32
            {
                // Collection has child collections, and this is not the first child
                let mut next_child = 1;
                while coll_last_written_child[collection_node_idx]
                    != coll_child_order[&(collection_node_idx, (next_child - 1))] as i32
                {
                    next_child += 1;
                }
                coll_last_written_child[collection_node_idx] =
                    coll_child_order[&(collection_node_idx, next_child)] as i32;
                collection_node_idx = coll_child_order[&(collection_node_idx, next_child)];

                if link_collection_nodes[collection_node_idx].is_alias() && !first_delimiter_node {
                    // Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
                    first_delimiter_node = true;
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::DelimiterUsage,
                        0,
                    ));
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::DelimiterClose,
                        0,
                    ));
                } else if link_collection_nodes[collection_node_idx].is_alias()
                    && first_delimiter_node
                {
                    main_item_list.insert(
                        1,
                        MainItemNode::new(
                            0,
                            0,
                            ItemNodeType::Collection,
                            0,
                            collection_node_idx,
                            MainItems::DelimiterUsage,
                            0,
                        ),
                    );
                } else if !link_collection_nodes[collection_node_idx].is_alias()
                    && first_delimiter_node
                {
                    main_item_list.insert(
                        1,
                        MainItemNode::new(
                            0,
                            0,
                            ItemNodeType::Collection,
                            0,
                            collection_node_idx,
                            MainItems::DelimiterUsage,
                            0,
                        ),
                    );
                    main_item_list.insert(
                        1,
                        MainItemNode::new(
                            0,
                            0,
                            ItemNodeType::Collection,
                            0,
                            collection_node_idx,
                            MainItems::DelimiterClose,
                            0,
                        ),
                    );
                    first_delimiter_node = false;
                }
                if !link_collection_nodes[collection_node_idx].is_alias() {
                    main_item_list.push(MainItemNode::new(
                        0,
                        0,
                        ItemNodeType::Collection,
                        0,
                        collection_node_idx,
                        MainItems::Collection,
                        0,
                    ));
                    actual_coll_level += 1;
                }
            } else {
                actual_coll_level -= 1;
                main_item_list.push(MainItemNode::new(
                    0,
                    0,
                    ItemNodeType::Collection,
                    0,
                    collection_node_idx,
                    MainItems::CollectionEnd,
                    0,
                ));
                collection_node_idx = link_collection_nodes[collection_node_idx].parent as usize;
            }
        }
    }

    // ****************************************************************
    // Inserted Input/Output/Feature main items into the main_item_list
    // in order of reconstructed bit positions
    // ****************************************************************
    for rt_idx in ReportType::values() {
        // Add all value caps to node list
        let mut first_delimiter_node = false;
        let caps_info = header.caps_info[rt_idx as usize];
        for caps_idx in caps_info.first_cap..caps_info.last_cap {
            let caps = caps_list[caps_idx as usize];
            let mut coll_begin = main_item_list
                .iter()
                .position(|node| node.collection_index == caps.link_collection as usize)
                .unwrap();
            let (first_bit, last_bit) = {
                let range = caps.get_bit_range();
                (range.first_bit, range.last_bit)
            };

            for child_idx in 0..coll_number_of_direct_childs[caps.link_collection as usize] {
                // Determine in which section before/between/after child collection the item should be inserted
                let child_first_bit = coll_child_order
                    .get(&(caps.link_collection as usize, child_idx))
                    .and_then(|i| coll_bit_range.get(&(*i, caps.report_id, rt_idx)))
                    .map(|r| r.first_bit)
                    .unwrap_or(0);
                if first_bit < child_first_bit {
                    // Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position
                    break;
                }
                let index = coll_child_order[&(caps.link_collection as usize, child_idx)];
                coll_begin = main_item_list
                    .iter()
                    .rposition(|node| node.collection_index == index)
                    .unwrap();
            }
            let list_node = 1 + search_list(
                first_bit as i32,
                rt_idx.into(),
                caps.report_id,
                coll_begin,
                &main_item_list,
            );

            // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
            // While the order in the WIN32 capabiliy strutures is the opposite:
            // Here the preferred usage is the last aliased usage in the sequence.

            if caps.is_alias() && !first_delimiter_node {
                // Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)
                first_delimiter_node = true;
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        MainItems::DelimiterUsage,
                        caps.report_id,
                    ),
                );
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        MainItems::DelimiterClose,
                        caps.report_id,
                    ),
                );
            } else if caps.is_alias() && first_delimiter_node {
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        MainItems::DelimiterUsage,
                        caps.report_id,
                    ),
                );
            } else if !caps.is_alias() && first_delimiter_node {
                // Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        MainItems::DelimiterUsage,
                        caps.report_id,
                    ),
                );
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        MainItems::DelimiterOpen,
                        caps.report_id,
                    ),
                );
                first_delimiter_node = false;
            }
            if !caps.is_alias() {
                main_item_list.insert(
                    list_node,
                    MainItemNode::new(
                        first_bit,
                        last_bit,
                        ItemNodeType::Cap,
                        caps_idx as i32,
                        caps.link_collection as usize,
                        rt_idx.into(),
                        caps.report_id,
                    ),
                );
            }
        }
    }

    // ***********************************************************
    // Add const main items for padding to main_item_list
    // -To fill all bit gaps
    // -At each report end for 8bit padding
    //  Note that information about the padding at the report end,
    //  is not stored in the preparsed data, but in practice all
    //  report descriptors seem to have it, as assumed here.
    // ***********************************************************
    {
        let mut last_bit_position: HashMap<(MainItems, u8), i32> = HashMap::new();
        let mut last_report_item_lookup: HashMap<(MainItems, u8), usize> = HashMap::new();

        let mut index = 0;
        while index < main_item_list.len() {
            let current = main_item_list[index];
            if ReportType::try_from(current.main_item_type).is_ok() {
                let lbp = last_bit_position
                    .get(&(current.main_item_type, current.report_id))
                    .copied()
                    .unwrap_or(-1);
                let lrip = last_report_item_lookup
                    .get(&(current.main_item_type, current.report_id))
                    .copied();
                if lbp + 1 != current.first_bit as i32
                    && lrip.is_some_and(|i| main_item_list[i].first_bit != current.first_bit)
                {
                    let list_node = search_list(
                        lbp,
                        current.main_item_type,
                        current.report_id,
                        lrip.unwrap(),
                        &main_item_list,
                    );
                    main_item_list.insert(
                        list_node + 1,
                        MainItemNode::new(
                            (lbp + 1) as u16,
                            current.first_bit - 1,
                            ItemNodeType::Padding,
                            -1,
                            0,
                            current.main_item_type,
                            current.report_id,
                        ),
                    );
                    index += 1;
                }
                last_bit_position.insert(
                    (current.main_item_type, current.report_id),
                    current.last_bit as i32,
                );
                last_report_item_lookup.insert((current.main_item_type, current.report_id), index);
            }
            index += 1;
        }

        for rt_idx in ReportType::values() {
            for report_idx in 0..=255 {
                if let Some(lbp) = last_bit_position.get(&(rt_idx.into(), report_idx)) {
                    let padding = 8 - ((*lbp + 1) % 8);
                    if padding < 8 {
                        // Insert padding item after item referenced in last_report_item_lookup
                        let lrip = *last_report_item_lookup
                            .get(&(rt_idx.into(), report_idx))
                            .unwrap();
                        main_item_list.insert(
                            lrip + 1,
                            MainItemNode::new(
                                (lbp + 1) as u16,
                                (lbp + padding) as u16,
                                ItemNodeType::Padding,
                                -1,
                                0,
                                rt_idx.into(),
                                report_idx,
                            ),
                        );
                        last_report_item_lookup
                            .values_mut()
                            .filter(|i| **i > lrip)
                            .for_each(|i| *i += 1);
                    }
                }
            }
        }
    }
    main_item_list
}

fn search_list(
    search_bit: i32,
    main_item_type: MainItems,
    report_id: u8,
    start: usize,
    list: &[MainItemNode],
) -> usize {
    list[start..]
        .iter()
        .peaking()
        .position(|(_, next)| {
            next.is_some_and(|next| {
                next.main_item_type == MainItems::Collection
                    || next.main_item_type == MainItems::CollectionEnd
                    || (next.last_bit as i32 >= search_bit
                        && next.report_id == report_id
                        && next.main_item_type == main_item_type)
            })
        })
        .unwrap()
        + start
}


================================================
FILE: src/windows_native/descriptor/tests.rs
================================================
use crate::windows_native::descriptor::get_descriptor_ptr;
use std::fs::read_to_string;

#[test]
fn test_01() {
    execute_testcase("045E_02FF_0005_0001");
}
#[test]
fn test_02() {
    execute_testcase("046A_0011_0006_0001");
}
#[test]
fn test_03() {
    execute_testcase("046D_0A37_0001_000C");
}
#[test]
fn test_04() {
    execute_testcase("046D_B010_0001_000C");
}
#[test]
fn test_05() {
    execute_testcase("046D_B010_0001_FF00");
}
#[test]
fn test_06() {
    execute_testcase("046D_B010_0002_0001");
}
#[test]
fn test_07() {
    execute_testcase("046D_B010_0002_FF00");
}
#[test]
fn test_08() {
    execute_testcase("046D_B010_0006_0001");
}
#[test]
fn test_09() {
    execute_testcase("046D_C077_0002_0001");
}
#[test]
fn test_10() {
    execute_testcase("046D_C283_0004_0001");
}
#[test]
fn test_11() {
    execute_testcase("046D_C52F_0001_000C");
}
#[test]
fn test_12() {
    execute_testcase("046D_C52F_0001_FF00");
}
#[test]
fn test_13() {
    execute_testcase("046D_C52F_0002_0001");
}
#[test]
fn test_14() {
    execute_testcase("046D_C52F_0002_FF00");
}
#[test]
fn test_15() {
    execute_testcase("046D_C534_0001_000C");
}
#[test]
fn test_16() {
    execute_testcase("046D_C534_0001_FF00");
}
#[test]
fn test_17() {
    execute_testcase("046D_C534_0002_0001");
}
#[test]
fn test_18() {
    execute_testcase("046D_C534_0002_FF00");
}
#[test]
fn test_19() {
    execute_testcase("046D_C534_0006_0001");
}
#[test]
fn test_20() {
    execute_testcase("046D_C534_0080_0001");
}
#[test]
fn test_21() {
    execute_testcase("047F_C056_0001_000C");
}
#[test]
fn test_22() {
    execute_testcase("047F_C056_0003_FFA0");
}
#[test]
fn test_23() {
    execute_testcase("047F_C056_0005_000B");
}
#[test]
fn test_24() {
    execute_testcase("17CC_1130_0000_FF01");
}

fn execute_testcase(filename: &str) {
    let source_path = format!("./tests/pp_data/{filename}.pp_data");
    let expected_path = format!("./tests/pp_data/{filename}.expected");
    println!("Testing: {:?} <-> {:?}", source_path, expected_path);
    let pp_data = decode_hex(&read_to_string(&source_path).unwrap());
    let expected_descriptor = decode_hex(&read_to_string(&expected_path).unwrap());
    let constructed_descriptor = unsafe { get_descriptor_ptr(pp_data.as_ptr() as _) }.unwrap();
    assert_eq!(constructed_descriptor, expected_descriptor);
}

fn decode_hex(hex: &str) -> Vec<u8> {
    hex.lines()
        .flat_map(|line| {
            line.split(',')
                .map(|hex| hex.trim())
                .filter(|hex| !hex.is_empty())
                .map(|hex| hex.strip_prefix("0x").unwrap())
                .map(|hex| u8::from_str_radix(hex, 16).unwrap())
        })
        .collect()
}


================================================
FILE: src/windows_native/descriptor/typedefs.rs
================================================
use crate::windows_native::descriptor::types::BitRange;
use std::mem::size_of;

// Reverse engineered typedefs for the internal structure of the preparsed data taken from
// https://github.com/libusb/hidapi/blob/master/windows/hidapi_descriptor_reconstruct.h
// https://github.com/libusb/hidapi/pull/306

#[macro_export]
macro_rules! const_assert {
    ($x:expr $(,)?) => {
        #[allow(unknown_lints)]
        const _: [(); 0 - !{
            const ASSERT: bool = $x;
            ASSERT
        } as usize] = [];
    };
}

pub type Usage = u16;

const_assert!(size_of::<LinkCollectionNode>() == 16);
#[derive(Copy, Clone)]
#[repr(C)]
pub struct LinkCollectionNode {
    pub link_usage: Usage,
    pub link_usage_page: Usage,
    pub parent: u16,
    pub number_of_children: u16,
    pub next_sibling: u16,
    pub first_child: u16,
    pub bits: u32,
}

impl LinkCollectionNode {
    pub fn is_alias(&self) -> bool {
        self.bits & 1u32 << 8 != 0
    }
    pub fn collection_type(&self) -> u8 {
        (self.bits & 0xFFu32) as u8
    }
}

const_assert!(size_of::<CapsInfo>() == 8);
#[derive(Copy, Clone)]
#[repr(C)]
pub struct CapsInfo {
    pub first_cap: u16,
    pub number_of_caps: u16,
    pub last_cap: u16,
    pub report_byte_length: u16,
}

const_assert!(size_of::<UnknownToken>() == 8);
#[derive(Copy, Clone)]
#[repr(C)]
pub struct UnknownToken {
    pub token: u8,
    _reserved: [u8; 3],
    pub bit_field: u32,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct Button {
    pub logical_min: i32,
    pub logical_max: i32,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct NotButton {
    pub has_nul: u8,
    _reserved: [u8; 3],
    pub logical_min: i32,
    pub logical_max: i32,
    pub physical_min: i32,
    pub physical_max: i32,
}

#[derive(Copy, Clone)]
#[repr(C)]
union MaybeButton {
    button: Button,
    not_button: NotButton,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct Range {
    pub usage_min: Usage,
    pub usage_max: Usage,
    pub string_min: u16,
    pub string_max: u16,
    pub designator_min: u16,
    pub designator_max: u16,
    pub data_index_min: u16,
    pub data_index_max: u16,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct NotRange {
    pub usage: Usage,
    _reserved1: Usage,
    pub string_index: u16,
    _reserved2: u16,
    pub designator_index: u16,
    _reserved3: u16,
    pub data_index: u16,
    _reserved4: u16,
}

#[derive(Copy, Clone)]
#[repr(C)]
union MaybeRange {
    range: Range,
    not_range: NotRange,
}

const_assert!(size_of::<Caps>() == 104);
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Caps {
    pub usage_page: Usage,
    pub report_id: u8,
    pub bit_position: u8,
    pub report_size: u16,
    pub report_count: u16,
    pub byte_position: u16,
    pub bit_count: u16,
    pub bit_field: u32,
    pub next_byte_position: u16,
    pub link_collection: u16,
    pub link_usage_page: Usage,
    pub link_usage: Usage,
    pub flags: u8,
    _reserved: [u8; 3],
    pub unknown_tokens: [UnknownToken; 4],
    maybe_range: MaybeRange,
    maybe_button: MaybeButton,
    pub units: u32,
    pub units_exp: u32,
}

impl Caps {
    pub fn is_button_cap(&self) -> bool {
        self.flags & (1 << 2) != 0
    }
    pub fn is_range(&self) -> bool {
        self.flags & (1 << 4) != 0
    }
    pub fn is_alias(&self) -> bool {
        self.flags & (1 << 5) != 0
    }
    pub fn is_string_range(&self) -> bool {
        self.flags & (1 << 6) != 0
    }
    pub fn is_designator_range(&self) -> bool {
        self.flags & (1 << 7) != 0
    }

    pub fn range(&self) -> Range {
        //Both union elements have the same size and are valid for all bit patterns
        unsafe { self.maybe_range.range }
    }

    pub fn not_range(&self) -> NotRange {
        //Both union elements have the same size and are valid for all bit patterns
        unsafe { self.maybe_range.not_range }
    }

    pub fn button(&self) -> Button {
        //Both union elements have the same size and are valid for all bit patterns
        unsafe { self.maybe_button.button }
    }

    pub fn not_button(&self) -> NotButton {
        //Both union elements have the same size and are valid for all bit patterns
        unsafe { self.maybe_button.not_button }
    }

    pub fn get_bit_range(&self) -> BitRange {
        let first_bit = (self.byte_position - 1) * 8 + self.bit_position as u16;
        let last_bit = first_bit + self.report_size * self.report_count - 1;
        BitRange {
            first_bit,
            last_bit,
        }
    }
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct HidpPreparsedData {
    pub magic_key: [u8; 8],
    pub usage: Usage,
    pub usage_page: Usage,
    _reserved: [u16; 2],
    pub caps_info: [CapsInfo; 3],
    pub first_byte_of_link_collection_array: u16,
    pub number_link_collection_nodes: u16,
}


================================================
FILE: src/windows_native/descriptor/types.rs
================================================
#![allow(dead_code)]

use std::fmt::Debug;

#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum ReportType {
    Input = 0x0,
    Output = 0x1,
    Feature = 0x2,
}
impl ReportType {
    pub const fn values() -> impl IntoIterator<Item = Self> {
        [Self::Input, Self::Output, Self::Feature]
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum Items {
    MainInput = 0x80,              // 1000 00 nn
    MainOutput = 0x90,             // 1001 00 nn
    MainFeature = 0xB0,            // 1011 00 nn
    MainCollection = 0xA0,         // 1010 00 nn
    MainCollectionEnd = 0xC0,      // 1100 00 nn
    GlobalUsagePage = 0x04,        // 0000 01 nn
    GlobalLogicalMinimum = 0x14,   // 0001 01 nn
    GlobalLogicalMaximum = 0x24,   // 0010 01 nn
    GlobalPhysicalMinimum = 0x34,  // 0011 01 nn
    GlobalPhysicalMaximum = 0x44,  // 0100 01 nn
    GlobalUnitExponent = 0x54,     // 0101 01 nn
    GlobalUnit = 0x64,             // 0110 01 nn
    GlobalReportSize = 0x74,       // 0111 01 nn
    GlobalReportId = 0x84,         // 1000 01 nn
    GlobalReportCount = 0x94,      // 1001 01 nn
    GlobalPush = 0xA4,             // 1010 01 nn
    GlobalPop = 0xB4,              // 1011 01 nn
    LocalUsage = 0x08,             // 0000 10 nn
    LocalUsageMinimum = 0x18,      // 0001 10 nn
    LocalUsageMaximum = 0x28,      // 0010 10 nn
    LocalDesignatorIndex = 0x38,   // 0011 10 nn
    LocalDesignatorMinimum = 0x48, // 0100 10 nn
    LocalDesignatorMaximum = 0x58, // 0101 10 nn
    LocalString = 0x78,            // 0111 10 nn
    LocalStringMinimum = 0x88,     // 1000 10 nn
    LocalStringMaximum = 0x98,     // 1001 10 nn
    LocalDelimiter = 0xA8,         // 1010 10 nn
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum MainItems {
    Input = ReportType::Input as u16,
    Output = ReportType::Output as u16,
    Feature = ReportType::Feature as u16,
    Collection,
    CollectionEnd,
    DelimiterOpen,
    DelimiterUsage,
    DelimiterClose,
}

impl From<ReportType> for MainItems {
    fn from(value: ReportType) -> Self {
        match value {
            ReportType::Input => Self::Input,
            ReportType::Output => Self::Output,
            ReportType::Feature => Self::Feature,
        }
    }
}

impl TryFrom<MainItems> for ReportType {
    type Error = ();

    fn try_from(value: MainItems) -> Result<Self, Self::Error> {
        match value {
            MainItems::Input => Ok(Self::Input),
            MainItems::Output => Ok(Self::Output),
            MainItems::Feature => Ok(Self::Feature),
            _ => Err(()),
        }
    }
}

#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub struct BitRange {
    pub first_bit: u16,
    pub last_bit: u16,
}

impl BitRange {
    pub fn merge(self, other: BitRange) -> BitRange {
        BitRange {
            first_bit: self.first_bit.min(other.first_bit),
            last_bit: self.last_bit.max(other.last_bit),
        }
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ItemNodeType {
    Cap,
    Padding,
    Collection,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct MainItemNode {
    pub first_bit: u16,
    pub last_bit: u16,
    pub node_type: ItemNodeType,
    pub caps_index: i32,
    pub collection_index: usize,
    pub main_item_type: MainItems,
    pub report_id: u8,
}

impl MainItemNode {
    pub fn new(
        first_bit: u16,
        last_bit: u16,
        node_type: ItemNodeType,
        caps_index: i32,
        collection_index: usize,
        main_item_type: MainItems,
        report_id: u8,
    ) -> Self {
        Self {
            first_bit,
            last_bit,
            node_type,
            caps_index,
            collection_index,
            main_item_type,
            report_id,
        }
    }
}


================================================
FILE: src/windows_native/dev_node.rs
================================================
use crate::windows_native::error::{check_config, WinError, WinResult};
use crate::windows_native::string::U16Str;
use crate::windows_native::types::{DeviceProperty, PropertyKey};
use std::ptr::null_mut;
use windows_sys::Win32::Devices::DeviceAndDriverInstallation::{
    CM_Get_DevNode_PropertyW, CM_Get_Parent, CM_Locate_DevNodeW, CM_LOCATE_DEVNODE_NORMAL,
    CR_BUFFER_SMALL, CR_SUCCESS,
};

#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DevNode(u32);

impl DevNode {
    pub fn from_device_id(device_id: &U16Str) -> WinResult<Self> {
        let mut node = 0;
        let cr =
            unsafe { CM_Locate_DevNodeW(&mut node, device_id.as_ptr(), CM_LOCATE_DEVNODE_NORMAL) };
        check_config(cr, CR_SUCCESS)?;
        Ok(Self(node))
    }

    pub fn parent(self) -> WinResult<Self> {
        let mut parent = 0;
        let cr = unsafe { CM_Get_Parent(&mut parent, self.0, 0) };
        check_config(cr, CR_SUCCESS)?;
        Ok(Self(parent))
    }

    fn get_property_size<T: DeviceProperty>(
        self,
        property_key: impl PropertyKey,
    ) -> WinResult<usize> {
        let mut property_type = 0;
        let mut len = 0;
        let cr = unsafe {
            CM_Get_DevNode_PropertyW(
                self.0,
                property_key.as_ptr(),
                &mut property_type,
                null_mut(),
                &mut len,
                0,
            )
        };
        check_config(cr, CR_BUFFER_SMALL)?;
        ensure!(
            property_type == T::TYPE,
            Err(WinError::WrongPropertyDataType)
        );
        Ok(len as usize)
    }

    pub fn get_property<T: DeviceProperty>(self, property_key: impl PropertyKey) -> WinResult<T> {
        let size = self.get_property_size::<T>(property_key)?;
        let mut property = T::create_sized(size);
        let mut property_type = 0;
        let mut len = size as u32;
        let cr = unsafe {
            CM_Get_DevNode_PropertyW(
                self.0,
                property_key.as_ptr(),
                &mut property_type,
                property.as_ptr_mut(),
                &mut len,
                0,
            )
        };
        check_config(cr, CR_SUCCESS)?;
        ensure!(size == len as usize, Err(WinError::UnexpectedReturnSize));
        property.validate();
        Ok(property)
    }
}


================================================
FILE: src/windows_native/device_info.rs
================================================
use crate::windows_native::dev_node::DevNode;
use crate::windows_native::error::WinResult;
use crate::windows_native::hid::{get_hid_attributes, PreparsedData};
use crate::windows_native::interfaces::Interface;
use crate::windows_native::string::{U16Str, U16String, U16StringList};
use crate::windows_native::types::{Handle, InternalBusType};
use crate::{BusType, DeviceInfo, WcharString};
use std::ffi::{c_void, CString};
use std::mem::{size_of, zeroed};
use windows_sys::core::GUID;
use windows_sys::Win32::Devices::HumanInterfaceDevice::{
    HidD_GetManufacturerString, HidD_GetProductString, HidD_GetSerialNumberString,
};
use windows_sys::Win32::Devices::Properties::{
    DEVPKEY_Device_CompatibleIds, DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId,
    DEVPKEY_Device_Manufacturer, DEVPKEY_NAME,
};
use windows_sys::Win32::Foundation::{HANDLE, PROPERTYKEY};

// These constants are only part of the heavyweight `windows` and not `windows-sys`,
// so we manually define them here.
#[allow(nonstandard_style)]
const PKEY_DeviceInterface_Bluetooth_DeviceAddress: PROPERTYKEY = PROPERTYKEY {
    fmtid: GUID::from_u128(0x2bd67d8b_8beb_48d5_87e0_6cda3428040a),
    pid: 1,
};
#[allow(nonstandard_style)]
const PKEY_DeviceInterface_Bluetooth_Manufacturer: PROPERTYKEY = PROPERTYKEY {
    fmtid: GUID::from_u128(0x2bd67d8b_8beb_48d5_87e0_6cda3428040a),
    pid: 4,
};
#[allow(nonstandard_style)]
const PKEY_DeviceInterface_Bluetooth_ModelNumber: PROPERTYKEY = PROPERTYKEY {
    fmtid: GUID::from_u128(0x2bd67d8b_8beb_48d5_87e0_6cda3428040a),
    pid: 5,
};

fn read_string(
    func: unsafe extern "system" fn(HANDLE, *mut c_void, u32) -> bool,
    handle: &Handle,
) -> WcharString {
    // Return empty string on failure to match the c implementation
    let mut string = [0u16; 256];
    if unsafe {
        func(
            handle.as_raw(),
            string.as_mut_ptr() as _,
            (size_of::<u16>() * string.len()) as u32,
        )
    } {
        U16Str::from_slice_list(&string)
            .map(WcharString::from)
            .next()
            .unwrap_or_else(|| WcharString::String(String::new()))
    } else {
        // WcharString::None
        WcharString::String(String::new())
    }
}

pub fn get_device_info(path: &U16Str, handle: &Handle) -> DeviceInfo {
    let attrib = get_hid_attributes(handle);
    let caps = PreparsedData::load(handle)
        .and_then(|data| data.get_caps())
        .unwrap_or(unsafe { zeroed() });
    let mut dev = DeviceInfo {
        path: CString::new(path.to_string()).unwrap(),
        vendor_id: attrib.VendorID,
        product_id: attrib.ProductID,
        serial_number: read_string(HidD_GetSerialNumberString, handle),
        release_number: attrib.VersionNumber,
        manufacturer_string: read_string(HidD_GetManufacturerString, handle),
        product_string: read_string(HidD_GetProductString, handle),
        usage_page: caps.UsagePage,
        usage: caps.Usage,
        interface_number: -1,
        bus_type: BusType::Unknown,
    };

    // If this fails just ignore it. The data might be incomplete but at least there is something
    let _ = get_internal_info(path, &mut dev);
    dev
}

fn get_internal_info(interface_path: &U16Str, dev: &mut DeviceInfo) -> WinResult<()> {
    let device_id: U16String = Interface::get_property(interface_path, DEVPKEY_Device_InstanceId)?;

    let dev_node = DevNode::from_device_id(&device_id)?.parent()?;

    let compatible_ids: U16StringList = dev_node.get_property(DEVPKEY_Device_CompatibleIds)?;

    let bus_type = compatible_ids
        .iter()
        .filter_map(|compatible_id| match compatible_id {
            // The hidapi c library uses `contains` instead of `starts_with`,
            // but as far as I can tell `starts_with` is a better choice
            // USB devices
            // https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
            // https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
            id if id.starts_with_ignore_case("USB") => Some(InternalBusType::Usb),
            // Bluetooth devices
            // https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device
            id if id.starts_with_ignore_case("BTHENUM") => Some(InternalBusType::Bluetooth),
            id if id.starts_with_ignore_case("BTHLEDEVICE") => Some(InternalBusType::BluetoothLE),
            // I2C devices
            // https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management
            id if id.starts_with_ignore_case("PNP0C50") => Some(InternalBusType::I2c),
            // SPI devices
            // https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi
            id if id.starts_with_ignore_case("PNP0C51") => Some(InternalBusType::Spi),
            _ => None,
        })
        .next()
        .unwrap_or(InternalBusType::Unknown);
    dev.bus_type = bus_type.into();
    match bus_type {
        InternalBusType::Usb => get_usb_info(dev, dev_node)?,
        InternalBusType::BluetoothLE => get_ble_info(dev, dev_node)?,
        _ => (),
    };

    Ok(())
}

fn get_usb_info(dev: &mut DeviceInfo, mut dev_node: DevNode) -> WinResult<()> {
    let mut device_id: U16String = dev_node.get_property(DEVPKEY_Device_InstanceId)?;

    device_id.make_uppercase_ascii();
    // Check for Xbox Common Controller class (XUSB) device.
    // https://docs.microsoft.com/windows/win32/xinput/directinput-and-xusb-devices
    // https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput
    //
    if extract_int_token_value(&device_id, "IG_").is_some() {
        dev_node = dev_node.parent()?;
    }

    let mut hardware_ids: U16StringList = dev_node.get_property(DEVPKEY_Device_HardwareIds)?;

    // Get additional information from USB device's Hardware ID
    // https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
    // https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-interfaces-not-grouped-in-collections
    //
    for hardware_id in hardware_ids.iter_mut() {
        hardware_id.make_uppercase_ascii();

        if dev.release_number == 0 {
            if let Some(release_number) = extract_int_token_value(hardware_id, "REV_") {
                dev.release_number = release_number as u16;
            }
        }
        if dev.interface_number == -1 {
            if let Some(interface_number) = extract_int_token_value(hardware_id, "MI_") {
                dev.interface_number = interface_number as i32;
            }
        }
    }

    // Try to get USB device manufacturer string if not provided by HidD_GetManufacturerString.
    if dev.manufacturer_string().map_or(true, str::is_empty) {
        if let Ok(manufacturer_string) =
            dev_node.get_property::<U16String>(DEVPKEY_Device_Manufacturer)
        {
            dev.manufacturer_string = (&*manufacturer_string).into();
        }
    }

    // Try to get USB device serial number if not provided by HidD_GetSerialNumberString.
    if dev.serial_number().map_or(true, str::is_empty) {
        let mut usb_dev_node = dev_node;
        if dev.interface_number != -1 {
            // Get devnode parent to reach out composite parent USB device.
            // https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-the-composite-parent-device
            usb_dev_node = dev_node.parent()?;
        }

        let device_id: U16String = usb_dev_node.get_property(DEVPKEY_Device_InstanceId)?;

        // Extract substring after last '\\' of Instance ID.
        // For USB devices it may contain device's serial number.
        // https://docs.microsoft.com/windows-hardware/drivers/install/instance-ids
        //
        if let Some(start) = device_id
            .as_slice()
            .rsplit(|c| *c != b'&' as u16)
            .next()
            .and_then(|s| s.iter().rposition(|c| *c != b'\\' as u16))
        {
            dev.serial_number = U16Str::from_slice(&device_id.as_slice()[(start + 1)..]).into();
        }
    }

    if dev.interface_number == -1 {
        dev.interface_number = 0;
    }

    Ok(())
}

// HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
// Request this info via dev node properties instead.
// https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html
fn get_ble_info(dev: &mut DeviceInfo, dev_node: DevNode) -> WinResult<()> {
    if dev.manufacturer_string().map_or(true, str::is_empty) {
        if let Ok(manufacturer_string) =
            dev_node.get_property::<U16String>(PKEY_DeviceInterface_Bluetooth_Manufacturer)
        {
            dev.manufacturer_string = manufacturer_string.into();
        }
    }

    if dev.serial_number().map_or(true, str::is_empty) {
        if let Ok(serial_number) =
            dev_node.get_property::<U16String>(PKEY_DeviceInterface_Bluetooth_DeviceAddress)
        {
            dev.serial_number = serial_number.into();
        }
    }

    if dev.product_string().map_or(true, str::is_empty) {
        let product_string = dev_node
            .get_property::<U16String>(PKEY_DeviceInterface_Bluetooth_ModelNumber)
            .or_else(|_| {
                // Fallback: Get devnode grandparent to reach out Bluetooth LE device node
                dev_node
                    .parent()
                    .and_then(|parent_dev_node| parent_dev_node.get_property(DEVPKEY_NAME))
            });
        if let Ok(product_string) = product_string {
            dev.product_string = product_string.into();
        }
    }

    Ok(())
}

fn extract_int_token_value(u16str: &U16Str, token: &str) -> Option<u32> {
    let start = u16str.find_index(token)? + token.encode_utf16().count();
    char::decode_utf16(u16str.as_slice()[start..].iter().copied())
        .map_while(|c| c.ok().and_then(|c| c.to_digit(16)))
        .reduce(|l, r| l * 16 + r)
}


================================================
FILE: src/windows_native/error.rs
================================================
use crate::HidError;
use windows_sys::Win32::Devices::DeviceAndDriverInstallation::*;
use windows_sys::Win32::Foundation::*;

pub type WinResult<T> = Result<T, WinError>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WinError {
    Config(CONFIGRET),
    Win32(Win32Error),
    BufferTooSmall,
    InvalidDeviceId,
    InvalidDeviceNode,
    NoSuchValue,
    WrongPropertyDataType,
    UnexpectedReturnSize,
    InvalidPreparsedData,
    WaitTimedOut,
}

impl WinError {
    pub fn last() -> Self {
        Self::from(Win32Error::last())
    }
}

impl From<WinError> for HidError {
    fn from(value: WinError) -> Self {
        match value {
            WinError::Win32(Win32Error::Generic(err)) => HidError::IoError {
                error: std::io::Error::from_raw_os_error(err as _),
            },
            err => HidError::HidApiError {
                message: format!("WinError: {:?}", err),
            },
        }
    }
}

fn config_to_error(ret: CONFIGRET) -> WinError {
    match ret {
        CR_BUFFER_SMALL => WinError::BufferTooSmall,
        CR_INVALID_DEVICE_ID => WinError::InvalidDeviceId,
        CR_INVALID_DEVNODE => WinError::InvalidDeviceNode,
        CR_NO_SUCH_VALUE => WinError::NoSuchValue,
        ret => WinError::Config(ret),
    }
}

pub fn check_config(ret: CONFIGRET, expected: CONFIGRET) -> WinResult<()> {
    if ret == expected {
        Ok(())
    } else {
        Err(config_to_error(ret))
    }
}

pub fn check_boolean(ret: bool) -> WinResult<()> {
    if !ret {
        Err(Win32Error::last().into())
    } else {
        Ok(())
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Win32Error {
    Generic(WIN32_ERROR),
    Success,
    IoPending,
    WaitTimedOut,
}

impl Win32Error {
    pub fn last() -> Self {
        match unsafe { GetLastError() } {
            NO_ERROR => Self::Success,
            ERROR_IO_PENDING => Self::IoPending,
            ERROR_IO_INCOMPLETE | WAIT_TIMEOUT => Self::WaitTimedOut,
            code => Self::Generic(code),
        }
    }

    //pub fn is_error(self) -> bool {
    //    !matches!(self, Win32Error::Success | Win32Error::IoPending)
    //}
}

impl From<Win32Error> for WinError {
    fn from(value: Win32Error) -> Self {
        match value {
            Win32Error::WaitTimedOut => Self::WaitTimedOut,
            err => Self::Win32(err),
        }
    }
}

impl From<Win32Error> for HidError {
    fn from(value: Win32Error) -> Self {
        HidError::from(WinError::from(value))
    }
}


================================================
FILE: src/windows_native/hid.rs
================================================
use crate::windows_native::error::{check_boolean, WinError, WinResult};
use crate::windows_native::types::Handle;
use std::ffi::c_void;
use std::mem::{size_of, zeroed};
use windows_sys::core::GUID;
use windows_sys::Win32::Devices::HumanInterfaceDevice::{
    HidD_FreePreparsedData, HidD_GetAttributes, HidD_GetHidGuid, HidD_GetPreparsedData,
    HidP_GetCaps, HIDD_ATTRIBUTES, HIDP_CAPS, HIDP_STATUS_SUCCESS,
};

pub fn get_interface_guid() -> GUID {
    unsafe {
        let mut guid = zeroed();
        HidD_GetHidGuid(&mut guid);
        guid
    }
}

pub fn get_hid_attributes(handle: &Handle) -> HIDD_ATTRIBUTES {
    unsafe {
        let mut attrib = HIDD_ATTRIBUTES {
            Size: size_of::<HIDD_ATTRIBUTES>() as u32,
            ..zeroed()
        };
        HidD_GetAttributes(handle.as_raw(), &mut attrib);
        attrib
    }
}

#[repr(transparent)]
pub struct PreparsedData(isize);

impl Drop for PreparsedData {
    fn drop(&mut self) {
        unsafe {
            HidD_FreePreparsedData(self.0);
        }
    }
}

impl PreparsedData {
    pub fn load(handle: &Handle) -> WinResult<Self> {
        let mut pp_data = 0;
        check_boolean(unsafe { HidD_GetPreparsedData(handle.as_raw(), &mut pp_data) })?;
        ensure!(pp_data != 0, Err(WinError::InvalidPreparsedData));
        Ok(Self(pp_data))
    }

    #[allow(dead_code)]
    pub fn as_ptr(&self) -> *const c_void {
        self.0 as _
    }

    pub fn get_caps(&self) -> WinResult<HIDP_CAPS> {
        unsafe {
            let mut caps = zeroed();
            let r = HidP_GetCaps(self.0, &mut caps);
            ensure!(
                r == HIDP_STATUS_SUCCESS,
                Err(WinError::InvalidPreparsedData)
            );
            Ok(caps)
        }
    }
}


================================================
FILE: src/windows_native/interfaces.rs
================================================
use crate::windows_native::error::{check_config, WinError, WinResult};
use crate::windows_native::hid::get_interface_guid;
use crate::windows_native::string::{U16Str, U16StringList};
use crate::windows_native::types::{DeviceProperty, PropertyKey};
use std::ptr::{null, null_mut};
use windows_sys::core::GUID;
use windows_sys::Win32::Devices::DeviceAndDriverInstallation::{
    CM_Get_Device_Interface_ListW, CM_Get_Device_Interface_List_SizeW,
    CM_Get_Device_Interface_PropertyW, CM_GET_DEVICE_INTERFACE_LIST_PRESENT, CR_BUFFER_SMALL,
    CR_SUCCESS,
};

pub struct Interface;

impl Interface {
    fn get_property_size<T: DeviceProperty>(
        interface: &U16Str,
        property_key: impl PropertyKey,
    ) -> WinResult<usize> {
        let mut property_type = 0;
        let mut len = 0;
        let cr = unsafe {
            CM_Get_Device_Interface_PropertyW(
                interface.as_ptr(),
                property_key.as_ptr(),
                &mut property_type,
                null_mut(),
                &mut len,
                0,
            )
        };
        check_config(cr, CR_BUFFER_SMALL)?;
        ensure!(
            property_type == T::TYPE,
            Err(WinError::WrongPropertyDataType)
        );
        Ok(len as usize)
    }

    pub fn get_property<T: DeviceProperty>(
        interface: &U16Str,
        property_key: impl PropertyKey,
    ) -> WinResult<T> {
        let size = Self::get_property_size::<T>(interface, property_key)?;
        let mut property = T::create_sized(size);
        let mut property_type = 0;
        let mut len = size as u32;
        let cr = unsafe {
            CM_Get_Device_Interface_PropertyW(
                interface.as_ptr(),
                property_key.as_ptr(),
                &mut property_type,
                property.as_ptr_mut(),
                &mut len,
                0,
            )
        };
        check_config(cr, CR_SUCCESS)?;
        ensure!(size == len as usize, Err(WinError::UnexpectedReturnSize));
        property.validate();
        Ok(property)
    }

    fn get_interface_list_length(interface: GUID) -> WinResult<usize> {
        let mut len = 0;
        let cr = unsafe {
            CM_Get_Device_Interface_List_SizeW(
                &mut len,
                &interface,
                null(),
                CM_GET_DEVICE_INTERFACE_LIST_PRESENT,
            )
        };
        check_config(cr, CR_SUCCESS)?;
        Ok(len as usize)
    }

    pub fn get_interface_list() -> WinResult<U16StringList> {
        let interface_class_guid = get_interface_guid();

        let mut device_interface_list = Vec::new();
        loop {
            device_interface_list.resize(Self::get_interface_list_length(interface_class_guid)?, 0);
            let cr = unsafe {
                CM_Get_Device_Interface_ListW(
                    &interface_class_guid,
                    null(),
                    device_interface_list.as_mut_ptr(),
                    device_interface_list.len() as u32,
                    CM_GET_DEVICE_INTERFACE_LIST_PRESENT,
                )
            };
            if cr == CR_SUCCESS {
                return Ok(U16StringList(device_interface_list));
            }
            check_config(cr, CR_BUFFER_SMALL)?;
        }
    }
}


================================================
FILE: src/windows_native/mod.rs
================================================
//! The implementation which uses the the raw win32 api to perform operations

macro_rules! ensure {
    ($cond:expr, $result:expr) => {
        if !($cond) {
            return $result;
        }
    };
}

mod descriptor;
mod dev_node;
mod device_info;
mod error;
mod hid;
mod interfaces;
mod string;
mod types;
mod utils;

use std::cell::{Cell, RefCell};
use std::ptr::{null, null_mut};
use std::{
    ffi::CStr,
    fmt::{self, Debug},
};

use crate::windows_native::dev_node::DevNode;
use crate::windows_native::device_info::get_device_info;
use crate::windows_native::error::{check_boolean, Win32Error, WinError, WinResult};
use crate::windows_native::hid::{get_hid_attributes, PreparsedData};
use crate::windows_native::interfaces::Interface;
use crate::windows_native::string::{U16Str, U16String};
use crate::windows_native::types::{Handle, Overlapped};
use crate::{DeviceInfo, HidDeviceBackendBase, HidDeviceBackendWindows, HidError, HidResult};
use windows_sys::core::GUID;
use windows_sys::Win32::Devices::HumanInterfaceDevice::{
    HidD_GetIndexedString, HidD_SetFeature, HidD_SetNumInputBuffers, HidD_SetOutputReport,
};
use windows_sys::Win32::Devices::Properties::{
    DEVPKEY_Device_ContainerId, DEVPKEY_Device_InstanceId,
};
use windows_sys::Win32::Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE, TRUE};
use windows_sys::Win32::Storage::FileSystem::{
    CreateFileW, ReadFile, WriteFile, FILE_FLAG_OVERLAPPED, FILE_SHARE_READ, FILE_SHARE_WRITE,
    OPEN_EXISTING,
};
use windows_sys::Win32::System::Threading::ResetEvent;
use windows_sys::Win32::System::IO::{CancelIoEx, DeviceIoControl};

const STRING_BUF_LEN: usize = 128;

pub struct HidApiBackend;
impl HidApiBackend {
    pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
        Ok(enumerate_devices(vid, pid)?)
    }

    pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
        open(vid, pid, None)
    }

    pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
        open(vid, pid, Some(sn))
    }

    pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
        open_path(device_path)
    }
}

/// Object for accessing HID device
pub struct HidDevice {
    device_handle: Handle,
    device_info: DeviceInfo,
    read_pending: Cell<bool>,
    blocking: Cell<bool>,
    read_state: RefCell<AsyncState>,
    write_state: RefCell<AsyncState>,
    feature_state: RefCell<AsyncState>,
}

unsafe impl Send for HidDevice {}

struct AsyncState {
    overlapped: Box<Overlapped>,
    buffer: Vec<u8>,
}

impl AsyncState {
    fn new(report_size: usize) -> Self {
        Self {
            overlapped: Default::default(),
            buffer: vec![0u8; report_size],
        }
    }

    fn clear_buffer(&mut self) {
        self.buffer.fill(0)
    }

    fn fill_buffer(&mut self, data: &[u8]) {
        // Make sure the right number of bytes are passed to WriteFile. Windows
        // expects the number of bytes which are in the _longest_ report (plus
        // one for the report number) bytes even if the data is a report
        // which is shorter than that. Windows gives us this value in
        // caps.OutputReportByteLength. If a user passes in fewer bytes than this,
        // use cached temporary buffer which is the proper size.
        let data_size = data.len().min(self.buffer.len());
        self.buffer[..data_size].copy_from_slice(&data[..data_size]);
        if data_size < self.buffer.len() {
            self.buffer[data_size..].fill(0);
        }
    }

    fn buffer_len(&self) -> usize {
        self.buffer.len()
    }

    fn buffer_ptr(&mut self) -> *mut u8 {
        self.buffer.as_mut_ptr()
    }
}

impl Debug for HidDevice {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("HidDevice").finish()
    }
}

impl HidDeviceBackendBase for HidDevice {
    fn write(&self, data: &[u8]) -> HidResult<usize> {
        ensure!(!data.is_empty(), Err(HidError::InvalidZeroSizeData));
        let mut state = self.write_state.borrow_mut();
        state.fill_buffer(data);

        let res = unsafe {
            WriteFile(
                self.device_handle.as_raw(),
                state.buffer_ptr(),
                state.buffer_len() as u32,
                null_mut(),
                state.overlapped.as_raw(),
            )
        };

        if res != TRUE {
            let err = Win32Error::last();
            ensure!(err == Win32Error::IoPending, Err(err.into()));
            Ok(state
                .overlapped
                .get_result(&self.device_handle, Some(1000))?)
        } else {
            Ok(0)
        }
    }

    fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
        self.read_timeout(buf, if self.blocking.get() { -1 } else { 0 })
    }

    fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
        ensure!(!buf.is_empty(), Err(HidError::InvalidZeroSizeData));
        let mut bytes_read = 0;
        let mut io_runnig = false;
        let mut state = self.read_state.borrow_mut();

        if !self.read_pending.get() {
            self.read_pending.set(true);
            state.clear_buffer();
            let res = unsafe {
                ResetEvent(state.overlapped.event_handle());
                ReadFile(
                    self.device_handle.as_raw(),
                    state.buffer_ptr() as _,
                    state.buffer_len() as u32,
                    &mut bytes_read,
                    state.overlapped.as_raw(),
                )
            };
            if res != TRUE {
                let err = Win32Error::last();
                if err != Win32Error::IoPending {
                    unsafe { CancelIoEx(self.device_handle.as_raw(), state.overlapped.as_raw()) };
                    self.read_pending.set(false);
                    return Err(err.into());
                }
                io_runnig = true;
            }
        } else {
            io_runnig = true;
        }

        if io_runnig {
            let res = state
                .overlapped
                .get_result(&self.device_handle, u32::try_from(timeout).ok());
            bytes_read = match res {
                Ok(written) => written as u32,
                //There was no data this time. Return zero bytes available, but leave the Overlapped I/O running.
                Err(WinError::WaitTimedOut) => return Ok(0),
                Err(err) => {
                    self.read_pending.set(false);
                    return Err(err.into());
                }
            };
        }
        self.read_pending.set(false);

        let mut copy_len = 0;
        if bytes_read > 0 {
            // If report numbers aren't being used, but Windows sticks a report
            // number (0x0) on the beginning of the report anyway. To make this
            // work like the other platforms, and to make it work more like the
            // HID spec, we'll skip over this byte.
            if state.buffer[0] == 0x0 {
                bytes_read -= 1;
                copy_len = usize::min(bytes_read as usize, buf.len());
                buf[..copy_len].copy_from_slice(&state.buffer[1..(1 + copy_len)]);
            } else {
                copy_len = usize::min(bytes_read as usize, buf.len());
                buf[..copy_len].copy_from_slice(&state.buffer[0..copy_len]);
            }
        }
        Ok(copy_len)
    }

    fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
        ensure!(!data.is_empty(), Err(HidError::InvalidZeroSizeData));
        let mut state = self.feature_state.borrow_mut();
        state.fill_buffer(data);

        check_boolean(unsafe {
            HidD_SetFeature(
                self.device_handle.as_raw(),
                state.buffer_ptr() as _,
                state.buffer_len() as u32,
            )
        })?;

        Ok(())
    }

    /// Set the first byte of `buf` to the 'Report ID' of the report to be read.
    /// Upon return, the first byte will still contain the Report ID, and the
    /// report data will start in `buf[1]`.
    fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
        #[allow(clippy::identity_op, clippy::double_parens)]
        const IOCTL_HID_GET_FEATURE: u32 = ((0x0000000b) << 16) | ((0) << 14) | ((100) << 2) | (2);
        ensure!(!buf.is_empty(), Err(HidError::InvalidZeroSizeData));
        let mut state = self.feature_state.borrow_mut();
        let mut bytes_returned = 0;

        let res = unsafe {
            ResetEvent(state.overlapped.event_handle());
            DeviceIoControl(
                self.device_handle.as_raw(),
                IOCTL_HID_GET_FEATURE,
                buf.as_mut_ptr() as _,
                buf.len() as u32,
                buf.as_mut_ptr() as _,
                buf.len() as u32,
                &mut bytes_returned,
                state.overlapped.as_raw(),
            )
        };
        if res != TRUE {
            let err = Win32Error::last();
            ensure!(err == Win32Error::IoPending, Err(err.into()))
        }

        bytes_returned = state.overlapped.get_result(&self.device_handle, None)? as u32;

        if buf[0] == 0x0 {
            bytes_returned += 1;
        }

        Ok(bytes_returned as usize)
    }

    fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
        ensure!(!data.is_empty(), Err(HidError::InvalidZeroSizeData));
        let mut state = self.feature_state.borrow_mut();
        state.fill_buffer(data);

        check_boolean(unsafe {
            HidD_SetOutputReport(
                self.device_handle.as_raw(),
                state.buffer_ptr() as _,
                state.buffer_len() as u32,
            )
        })?;

        Ok(())
    }

    fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
        self.blocking.set(blocking);
        Ok(())
    }

    fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
        Ok(self.device_info.manufacturer_string().map(String::from))
    }

    fn get_product_string(&self) -> HidResult<Option<String>> {
        Ok(self.device_info.product_string().map(String::from))
    }

    fn get_serial_number_string(&self) -> HidResult<Option<String>> {
        Ok(self.device_info.serial_number().map(String::from))
    }

    fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
        let mut buf = [0u16; STRING_BUF_LEN];
        let res = unsafe {
            HidD_GetIndexedString(
                self.device_handle.as_raw(),
                index as u32,
                buf.as_mut_ptr() as _,
                STRING_BUF_LEN as u32,
            )
        };
        check_boolean(res)?;
        Ok(buf.split(|c| *c == 0).map(String::from_utf16_lossy).next())
    }

    fn get_device_info(&self) -> HidResult<DeviceInfo> {
        Ok(self.device_info.clone())
    }

    fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
        let desc = descriptor::get_descriptor(&PreparsedData::load(&self.device_handle)?)?;
        let size = buf.len().min(desc.len());
        buf[..size].copy_from_slice(&desc[..size]);
        Ok(size)
    }
}

impl HidDeviceBackendWindows for HidDevice {
    fn get_container_id(&self) -> HidResult<GUID> {
        let path =
            U16String::try_from(self.device_info.path()).expect("device path is not valid unicode");

        let device_id: U16String = Interface::get_property(&path, DEVPKEY_Device_InstanceId)?;

        let dev_node = DevNode::from_device_id(&device_id)?;
        let guid = dev_node.get_property(DEVPKEY_Device_ContainerId)?;
        Ok(guid)
    }
}

impl Drop for HidDevice {
    fn drop(&mut self) {
        unsafe {
            for state in [
                &mut self.read_state,
                &mut self.write_state,
                &mut self.feature_state,
            ] {
                let mut state = state.borrow_mut();
                if CancelIoEx(self.device_handle.as_raw(), state.overlapped.as_raw()) > 0 {
                    _ = state.overlapped.get_result(&self.device_handle, None);
                }
            }
        }
    }
}

fn enumerate_devices(vendor_id: u16, product_id: u16) -> WinResult<Vec<DeviceInfo>> {
    Ok(Interface::get_interface_list()?
       
Download .txt
gitextract_v0dbt8oc/

├── .github/
│   └── workflows/
│       └── rust.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── Cargo.toml
├── LICENSE.txt
├── README.md
├── build.rs
├── examples/
│   ├── co2mon.rs
│   ├── dump_descriptors.rs
│   ├── logitech_gprox.rs
│   ├── lshid.rs
│   ├── open_first_device.rs
│   ├── readhid.rs
│   └── static_lifetime_bound.rs
├── src/
│   ├── error.rs
│   ├── ffi.rs
│   ├── hidapi/
│   │   ├── macos.rs
│   │   └── windows.rs
│   ├── hidapi.rs
│   ├── lib.rs
│   ├── linux_native/
│   │   └── ioctl.rs
│   ├── linux_native.rs
│   ├── macos.rs
│   ├── windows.rs
│   └── windows_native/
│       ├── descriptor/
│       │   ├── encoder.rs
│       │   ├── mod.rs
│       │   ├── tests.rs
│       │   ├── typedefs.rs
│       │   └── types.rs
│       ├── dev_node.rs
│       ├── device_info.rs
│       ├── error.rs
│       ├── hid.rs
│       ├── interfaces.rs
│       ├── mod.rs
│       ├── string.rs
│       ├── types.rs
│       └── utils.rs
└── tests/
    ├── assets/
    │   ├── mouse1.data
    │   └── mouse2.data
    └── pp_data/
        ├── 045E_02FF_0005_0001.expected
        ├── 045E_02FF_0005_0001.pp_data
        ├── 046A_0011_0006_0001.expected
        ├── 046A_0011_0006_0001.pp_data
        ├── 046D_0A37_0001_000C.expected
        ├── 046D_0A37_0001_000C.pp_data
        ├── 046D_B010_0001_000C.expected
        ├── 046D_B010_0001_000C.pp_data
        ├── 046D_B010_0001_FF00.expected
        ├── 046D_B010_0001_FF00.pp_data
        ├── 046D_B010_0002_0001.expected
        ├── 046D_B010_0002_0001.pp_data
        ├── 046D_B010_0002_FF00.expected
        ├── 046D_B010_0002_FF00.pp_data
        ├── 046D_B010_0006_0001.expected
        ├── 046D_B010_0006_0001.pp_data
        ├── 046D_C077_0002_0001.expected
        ├── 046D_C077_0002_0001.pp_data
        ├── 046D_C283_0004_0001.expected
        ├── 046D_C283_0004_0001.pp_data
        ├── 046D_C52F_0001_000C.expected
        ├── 046D_C52F_0001_000C.pp_data
        ├── 046D_C52F_0001_FF00.expected
        ├── 046D_C52F_0001_FF00.pp_data
        ├── 046D_C52F_0002_0001.expected
        ├── 046D_C52F_0002_0001.pp_data
        ├── 046D_C52F_0002_FF00.expected
        ├── 046D_C52F_0002_FF00.pp_data
        ├── 046D_C534_0001_000C.expected
        ├── 046D_C534_0001_000C.pp_data
        ├── 046D_C534_0001_FF00.expected
        ├── 046D_C534_0001_FF00.pp_data
        ├── 046D_C534_0002_0001.expected
        ├── 046D_C534_0002_0001.pp_data
        ├── 046D_C534_0002_FF00.expected
        ├── 046D_C534_0002_FF00.pp_data
        ├── 046D_C534_0006_0001.expected
        ├── 046D_C534_0006_0001.pp_data
        ├── 046D_C534_0080_0001.expected
        ├── 046D_C534_0080_0001.pp_data
        ├── 047F_C056_0001_000C.expected
        ├── 047F_C056_0001_000C.pp_data
        ├── 047F_C056_0003_FFA0.expected
        ├── 047F_C056_0003_FFA0.pp_data
        ├── 047F_C056_0005_000B.expected
        ├── 047F_C056_0005_000B.pp_data
        ├── 17CC_1130_0000_FF01.expected
        └── 17CC_1130_0000_FF01.pp_data
Download .txt
SYMBOL INDEX (432 symbols across 32 files)

FILE: build.rs
  function main (line 25) | fn main() {
  function compile_linux (line 48) | fn compile_linux() {
  function compile_freebsd (line 123) | fn compile_freebsd() {
  function compile_openbsd (line 129) | fn compile_openbsd() {
  function compile_illumos (line 135) | fn compile_illumos() {
  function compile_windows (line 174) | fn compile_windows() {
  function compile_macos (line 194) | fn compile_macos() {

FILE: examples/co2mon.rs
  constant CODE_TEMPERATURE (line 14) | const CODE_TEMPERATURE: u8 = 0x42;
  constant CODE_CONCENTRATION (line 15) | const CODE_CONCENTRATION: u8 = 0x50;
  constant HID_TIMEOUT (line 16) | const HID_TIMEOUT: i32 = 5000;
  constant DEV_VID (line 17) | const DEV_VID: u16 = 0x04d9;
  constant DEV_PID (line 18) | const DEV_PID: u16 = 0xa052;
  constant PACKET_SIZE (line 19) | const PACKET_SIZE: usize = 8;
  type Packet (line 21) | type Packet = [u8; PACKET_SIZE];
  type CO2Result (line 23) | enum CO2Result {
  function decode_temperature (line 30) | fn decode_temperature(value: u16) -> f32 {
  function decrypt (line 34) | fn decrypt(buf: Packet) -> Packet {
  function decode_buf (line 55) | fn decode_buf(buf: Packet) -> CO2Result {
  function invalid_data_err (line 85) | fn invalid_data_err(msg: impl Into<String>) -> HidError {
  function main (line 91) | fn main() -> Result<(), HidError> {

FILE: examples/dump_descriptors.rs
  function main (line 3) | fn main() {

FILE: examples/logitech_gprox.rs
  function main (line 12) | fn main() -> Result<(), Box<dyn Error>> {

FILE: examples/lshid.rs
  function main (line 13) | fn main() {

FILE: examples/open_first_device.rs
  function main (line 14) | fn main() {

FILE: examples/readhid.rs
  function main (line 15) | fn main() {

FILE: examples/static_lifetime_bound.rs
  function main (line 15) | fn main() {
  function requires_static_lt_bound (line 19) | fn requires_static_lt_bound<F: Fn() + 'static>(f: F) {
  function test_lt (line 23) | fn test_lt() -> Rc<HidDevice> {

FILE: src/error.rs
  type HidError (line 14) | pub enum HidError {
    method from (line 73) | fn from(e: std::io::Error) -> Self {
    method from (line 80) | fn from(e: nix::errno::Errno) -> Self {
  method fmt (line 41) | fn fmt(&self, f: &mut Formatter<'_>) -> Result {

FILE: src/ffi.rs
  type HidBusType (line 10) | type HidBusType = crate::BusType;
  type HidDevice (line 11) | pub type HidDevice = c_void;
  type LibusbContext (line 12) | type LibusbContext = c_void;
  type HidDeviceInfo (line 15) | pub struct HidDeviceInfo {
  function hid_init (line 33) | pub fn hid_init() -> c_int;
  function hid_exit (line 34) | pub fn hid_exit() -> c_int;
  function hid_enumerate (line 35) | pub fn hid_enumerate(vendor_id: c_ushort, product_id: c_ushort) -> *mut ...
  function hid_free_enumeration (line 36) | pub fn hid_free_enumeration(hid_device_info: *mut HidDeviceInfo);
  function hid_open (line 37) | pub fn hid_open(
  function hid_open_path (line 42) | pub fn hid_open_path(path: *const c_char) -> *mut HidDevice;
  function hid_libusb_wrap_sys_device (line 44) | pub fn hid_libusb_wrap_sys_device(sys_dev: intptr_t, interface_num: c_in...
  function libusb_set_option (line 46) | pub fn libusb_set_option(ctx: *mut LibusbContext, option: c_int);
  function hid_write (line 47) | pub fn hid_write(device: *mut HidDevice, data: *const c_uchar, length: s...
  function hid_read_timeout (line 48) | pub fn hid_read_timeout(
  function hid_read (line 54) | pub fn hid_read(device: *mut HidDevice, data: *mut c_uchar, length: size...
  function hid_set_nonblocking (line 55) | pub fn hid_set_nonblocking(device: *mut HidDevice, nonblock: c_int) -> c...
  function hid_send_feature_report (line 56) | pub fn hid_send_feature_report(
  function hid_get_feature_report (line 61) | pub fn hid_get_feature_report(
  function hid_send_output_report (line 66) | pub fn hid_send_output_report(
  function hid_get_input_report (line 71) | pub fn hid_get_input_report(
  function hid_close (line 76) | pub fn hid_close(device: *mut HidDevice);
  function hid_get_manufacturer_string (line 77) | pub fn hid_get_manufacturer_string(
  function hid_get_product_string (line 82) | pub fn hid_get_product_string(
  function hid_get_serial_number_string (line 87) | pub fn hid_get_serial_number_string(
  function hid_get_device_info (line 92) | pub fn hid_get_device_info(device: *mut HidDevice) -> *mut HidDeviceInfo;
  function hid_get_indexed_string (line 93) | pub fn hid_get_indexed_string(
  function hid_get_report_descriptor (line 99) | pub fn hid_get_report_descriptor(
  function hid_error (line 104) | pub fn hid_error(device: *mut HidDevice) -> *const wchar_t;
  function hid_darwin_get_location_id (line 113) | pub fn hid_darwin_get_location_id(device: *mut HidDevice, location_id: *...
  function hid_darwin_set_open_exclusive (line 114) | pub fn hid_darwin_set_open_exclusive(open_exclusive: c_int);
  function hid_darwin_get_open_exclusive (line 115) | pub fn hid_darwin_get_open_exclusive() -> c_int;
  function hid_darwin_is_device_open_exclusive (line 116) | pub fn hid_darwin_is_device_open_exclusive(device: *mut HidDevice) -> c_...
  function hid_winapi_get_container_id (line 127) | pub fn hid_winapi_get_container_id(

FILE: src/hidapi.rs
  constant STRING_BUF_LEN (line 17) | const STRING_BUF_LEN: usize = 128;
  type HidApiBackend (line 19) | pub struct HidApiBackend;
    method get_hid_device_info_vector (line 22) | pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec...
    method open (line 42) | pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
    method open_serial (line 55) | pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevic...
    method open_path (line 69) | pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
    method check_error (line 82) | pub fn check_error() -> HidResult<HidError> {
  function wchar_to_string (line 95) | unsafe fn wchar_to_string(wstr: *const wchar_t) -> WcharString {
  function conv_hid_device_info (line 131) | pub unsafe fn conv_hid_device_info(src: *mut ffi::HidDeviceInfo) -> HidR...
  type HidDevice (line 148) | pub struct HidDevice {
    method from_raw (line 153) | pub fn from_raw(device: *mut ffi::HidDevice) -> Self {
    method check_size (line 177) | fn check_size(&self, res: i32) -> HidResult<usize> {
  method fmt (line 163) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method drop (line 169) | fn drop(&mut self) {
  method check_error (line 190) | fn check_error(&self) -> HidResult<HidError> {
  method write (line 201) | fn write(&self, data: &[u8]) -> HidResult<usize> {
  method read (line 209) | fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
  method read_timeout (line 214) | fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
  method send_feature_report (line 226) | fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
  method get_feature_report (line 247) | fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
  method send_output_report (line 254) | fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
  method get_input_report (line 272) | fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
  method set_blocking_mode (line 282) | fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
  method get_manufacturer_string (line 298) | fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
  method get_product_string (line 311) | fn get_product_string(&self) -> HidResult<Option<String>> {
  method get_serial_number_string (line 324) | fn get_serial_number_string(&self) -> HidResult<Option<String>> {
  method get_indexed_string (line 337) | fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
  method get_device_info (line 351) | fn get_device_info(&self) -> HidResult<DeviceInfo> {
  method get_report_descriptor (line 362) | fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {

FILE: src/hidapi/macos.rs
  method get_location_id (line 7) | fn get_location_id(&self) -> HidResult<u32> {
  method is_open_exclusive (line 24) | fn is_open_exclusive(&self) -> HidResult<bool> {

FILE: src/hidapi/windows.rs
  method get_container_id (line 10) | fn get_container_id(&self) -> HidResult<GUID> {

FILE: src/lib.rs
  type HidResult (line 126) | pub type HidResult<T> = Result<T, HidError>;
  constant MAX_REPORT_DESCRIPTOR_SIZE (line 127) | pub const MAX_REPORT_DESCRIPTOR_SIZE: usize = 4096;
  type ContextState (line 129) | struct ContextState {
  type InitState (line 134) | enum InitState {
  type HidApi (line 152) | pub struct HidApi {
    method new (line 161) | pub fn new() -> HidResult<Self> {
    method disable_device_discovery (line 210) | pub fn disable_device_discovery() {
    method new_without_enumerate (line 232) | pub fn new_without_enumerate() -> HidResult<Self> {
    method refresh_devices (line 240) | pub fn refresh_devices(&mut self) -> HidResult<()> {
    method reset_devices (line 247) | pub fn reset_devices(&mut self) -> HidResult<()> {
    method add_devices (line 254) | pub fn add_devices(&mut self, vid: u16, pid: u16) -> HidResult<()> {
    method device_list (line 262) | pub fn device_list(&self) -> impl Iterator<Item = &DeviceInfo> {
    method open (line 271) | pub fn open(&self, vid: u16, pid: u16) -> HidResult<HidDevice> {
    method open_serial (line 278) | pub fn open_serial(&self, vid: u16, pid: u16, sn: &str) -> HidResult<H...
    method open_path (line 286) | pub fn open_path(&self, device_path: &CStr) -> HidResult<HidDevice> {
    method wrap_sys_device (line 293) | pub fn wrap_sys_device(&self, sys_dev: isize, interface_num: i32) -> H...
    method check_error (line 317) | pub fn check_error(&self) -> HidResult<HidError> {
  type WcharString (line 324) | enum WcharString {
  function from (line 332) | fn from(val: WcharString) -> Self {
  type BusType (line 343) | pub enum BusType {
  type DeviceInfo (line 357) | pub struct DeviceInfo {
    method path (line 374) | pub fn path(&self) -> &CStr {
    method vendor_id (line 378) | pub fn vendor_id(&self) -> u16 {
    method product_id (line 382) | pub fn product_id(&self) -> u16 {
    method serial_number (line 387) | pub fn serial_number(&self) -> Option<&str> {
    method serial_number_raw (line 394) | pub fn serial_number_raw(&self) -> Option<&[wchar_t]> {
    method release_number (line 401) | pub fn release_number(&self) -> u16 {
    method manufacturer_string (line 406) | pub fn manufacturer_string(&self) -> Option<&str> {
    method manufacturer_string_raw (line 413) | pub fn manufacturer_string_raw(&self) -> Option<&[wchar_t]> {
    method product_string (line 421) | pub fn product_string(&self) -> Option<&str> {
    method product_string_raw (line 428) | pub fn product_string_raw(&self) -> Option<&[wchar_t]> {
    method usage_page (line 437) | pub fn usage_page(&self) -> u16 {
    method usage (line 443) | pub fn usage(&self) -> u16 {
    method interface_number (line 447) | pub fn interface_number(&self) -> i32 {
    method bus_type (line 451) | pub fn bus_type(&self) -> BusType {
    method open_device (line 464) | pub fn open_device(&self, hidapi: &HidApi) -> HidResult<HidDevice> {
    method fmt (line 478) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type HidDeviceBackendBase (line 487) | trait HidDeviceBackendBase {
    method check_error (line 489) | fn check_error(&self) -> HidResult<HidError>;
    method write (line 490) | fn write(&self, data: &[u8]) -> HidResult<usize>;
    method read (line 491) | fn read(&self, buf: &mut [u8]) -> HidResult<usize>;
    method read_timeout (line 492) | fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize>;
    method send_feature_report (line 493) | fn send_feature_report(&self, data: &[u8]) -> HidResult<()>;
    method get_feature_report (line 494) | fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize>;
    method send_output_report (line 495) | fn send_output_report(&self, data: &[u8]) -> HidResult<()>;
    method get_input_report (line 497) | fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize>;
    method set_blocking_mode (line 498) | fn set_blocking_mode(&self, blocking: bool) -> HidResult<()>;
    method get_device_info (line 499) | fn get_device_info(&self) -> HidResult<DeviceInfo>;
    method get_manufacturer_string (line 500) | fn get_manufacturer_string(&self) -> HidResult<Option<String>>;
    method get_product_string (line 501) | fn get_product_string(&self) -> HidResult<Option<String>>;
    method get_serial_number_string (line 502) | fn get_serial_number_string(&self) -> HidResult<Option<String>>;
    method get_report_descriptor (line 503) | fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize>;
    method get_indexed_string (line 505) | fn get_indexed_string(&self, _index: i32) -> HidResult<Option<String>> {
  type HidDevice (line 512) | pub struct HidDevice {
    method from_backend (line 523) | fn from_backend(inner: Box<dyn HidDeviceBackend>) -> Self {
    method check_error (line 539) | pub fn check_error(&self) -> HidResult<HidError> {
    method write (line 559) | pub fn write(&self, data: &[u8]) -> HidResult<usize> {
    method read (line 570) | pub fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
    method read_timeout (line 582) | pub fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<...
    method send_feature_report (line 598) | pub fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
    method get_feature_report (line 610) | pub fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
    method send_output_report (line 627) | pub fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
    method get_input_report (line 640) | pub fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
    method set_blocking_mode (line 649) | pub fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
    method get_manufacturer_string (line 654) | pub fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
    method get_product_string (line 659) | pub fn get_product_string(&self) -> HidResult<Option<String>> {
    method get_serial_number_string (line 664) | pub fn get_serial_number_string(&self) -> HidResult<Option<String>> {
    method get_indexed_string (line 669) | pub fn get_indexed_string(&self, index: i32) -> HidResult<Option<Strin...
    method get_report_descriptor (line 679) | pub fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
    method get_device_info (line 684) | pub fn get_device_info(&self) -> HidResult<DeviceInfo> {
  method fmt (line 517) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

FILE: src/linux_native.rs
  constant BUS_USB (line 35) | const BUS_USB: u16 = 0x03;
  constant BUS_BLUETOOTH (line 36) | const BUS_BLUETOOTH: u16 = 0x05;
  constant BUS_I2C (line 37) | const BUS_I2C: u16 = 0x18;
  constant BUS_SPI (line 38) | const BUS_SPI: u16 = 0x1C;
  type HidApiBackend (line 40) | pub struct HidApiBackend;
    method get_hid_device_info_vector (line 43) | pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec...
    method open (line 66) | pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
    method open_serial (line 70) | pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevic...
    method open_path (line 74) | pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
  function device_to_hid_device_info (line 79) | fn device_to_hid_device_info(raw_device: &udev::Device) -> Option<Vec<De...
  function fill_in_usb (line 167) | fn fill_in_usb(device: &udev::Device, info: DeviceInfo, name: &OsStr) ->...
  type HidrawReportDescriptor (line 198) | struct HidrawReportDescriptor(Vec<u8>);
    method from_syspath (line 202) | pub fn from_syspath(syspath: &Path) -> HidResult<Self> {
    method from_slice (line 216) | pub fn from_slice(value: &[u8]) -> HidResult<Self> {
    method usages (line 220) | pub fn usages(&self) -> impl Iterator<Item = (u16, u16)> + '_ {
  type UsageIterator (line 229) | struct UsageIterator<'a> {
  type Item (line 235) | type Item = (u16, u16);
  method next (line 237) | fn next(&mut self) -> Option<Self::Item> {
  function next_hid_usage (line 247) | fn next_hid_usage(cursor: &mut Cursor<&Vec<u8>>, mut usage_page: u16) ->...
  function hid_item_size (line 318) | fn hid_item_size(key: u8, cursor: &mut Cursor<&Vec<u8>>) -> Option<(usiz...
  function hid_report_bytes (line 340) | fn hid_report_bytes(cursor: &mut Cursor<&Vec<u8>>, num_bytes: usize) -> ...
  function attribute_as_wchar (line 348) | fn attribute_as_wchar(dev: &udev::Device, attr: &str) -> WcharString {
  function attribute_as_i32 (line 358) | fn attribute_as_i32(dev: &udev::Device, attr: &str) -> Option<i32> {
  function attribute_as_u16 (line 367) | fn attribute_as_u16(dev: &udev::Device, attr: &str) -> Option<u16> {
  function osstring_to_string (line 374) | fn osstring_to_string(s: OsString) -> WcharString {
  function parse_hid_vid_pid (line 403) | fn parse_hid_vid_pid(s: &str) -> Option<(u16, u16, u16)> {
  type HidDevice (line 413) | pub struct HidDevice {
    method open (line 423) | pub(crate) fn open(vid: u16, pid: u16, sn: Option<&str>) -> HidResult<...
    method open_path (line 442) | pub(crate) fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
    method info (line 473) | fn info(&self) -> HidResult<Ref<'_, DeviceInfo>> {
  method as_fd (line 485) | fn as_fd(&self) -> BorrowedFd<'_> {
  method write (line 491) | fn write(&self, data: &[u8]) -> HidResult<usize> {
  method read (line 499) | fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
  method read_timeout (line 505) | fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
  method send_feature_report (line 534) | fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
  method get_feature_report (line 558) | fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
  method send_output_report (line 571) | fn send_output_report(&self, buf: &[u8]) -> HidResult<()> {
  method get_input_report (line 591) | fn get_input_report(&self, data: &mut [u8]) -> HidResult<usize> {
  method set_blocking_mode (line 600) | fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
  method get_manufacturer_string (line 605) | fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
  method get_product_string (line 610) | fn get_product_string(&self) -> HidResult<Option<String>> {
  method get_serial_number_string (line 615) | fn get_serial_number_string(&self) -> HidResult<Option<String>> {
  method get_device_info (line 620) | fn get_device_info(&self) -> HidResult<DeviceInfo> {
  method get_report_descriptor (line 637) | fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
  function test_parse_hid_vid_pid (line 653) | fn test_parse_hid_vid_pid() {
  function test_hidraw_report_descriptor_1 (line 660) | fn test_hidraw_report_descriptor_1() {
  function test_hidraw_report_descriptor_2 (line 669) | fn test_hidraw_report_descriptor_2() {
  function invalid_string_conversion (line 679) | fn invalid_string_conversion() {

FILE: src/linux_native/ioctl.rs
  constant HIDRAW_IOC_MAGIC (line 6) | const HIDRAW_IOC_MAGIC: u8 = b'H';
  constant HIDRAW_IOC_GRDESCSIZE (line 7) | const HIDRAW_IOC_GRDESCSIZE: u8 = 0x01;
  constant HIDRAW_SET_FEATURE (line 8) | const HIDRAW_SET_FEATURE: u8 = 0x06;
  constant HIDRAW_GET_FEATURE (line 9) | const HIDRAW_GET_FEATURE: u8 = 0x07;
  constant HIDRAW_SET_OUTPUT (line 10) | const HIDRAW_SET_OUTPUT: u8 = 0x0b;
  constant HIDRAW_GET_INPUT (line 11) | const HIDRAW_GET_INPUT: u8 = 0x0a;

FILE: src/macos.rs
  method set_open_exclusive (line 14) | pub fn set_open_exclusive(&self, exclusive: bool) {
  method get_open_exclusive (line 19) | pub fn get_open_exclusive(&self) -> bool {
  method get_location_id (line 26) | pub fn get_location_id(&self) -> HidResult<u32> {
  method is_open_exclusive (line 31) | pub fn is_open_exclusive(&self) -> HidResult<bool> {

FILE: src/windows.rs
  method get_container_id (line 10) | pub fn get_container_id(&self) -> HidResult<GUID> {

FILE: src/windows_native/descriptor/encoder.rs
  function encode_descriptor (line 6) | pub fn encode_descriptor(
  type DescriptorWriter (line 394) | struct DescriptorWriter(Vec<u8>);
    method write (line 398) | fn write(&mut self, item: Items, data: impl Into<i64>) -> WinResult<()> {
    method finish (line 439) | fn finish(self) -> Vec<u8> {

FILE: src/windows_native/descriptor/mod.rs
  function get_descriptor (line 19) | pub fn get_descriptor(pp_data: &PreparsedData) -> WinResult<Vec<u8>> {
  function get_descriptor_ptr (line 23) | unsafe fn get_descriptor_ptr(pp_data: *const c_void) -> WinResult<Vec<u8...
  function extract_structures (line 31) | unsafe fn extract_structures<'a>(
  function reconstruct_descriptor (line 61) | fn reconstruct_descriptor(
  function search_list (line 623) | fn search_list(

FILE: src/windows_native/descriptor/tests.rs
  function test_01 (line 5) | fn test_01() {
  function test_02 (line 9) | fn test_02() {
  function test_03 (line 13) | fn test_03() {
  function test_04 (line 17) | fn test_04() {
  function test_05 (line 21) | fn test_05() {
  function test_06 (line 25) | fn test_06() {
  function test_07 (line 29) | fn test_07() {
  function test_08 (line 33) | fn test_08() {
  function test_09 (line 37) | fn test_09() {
  function test_10 (line 41) | fn test_10() {
  function test_11 (line 45) | fn test_11() {
  function test_12 (line 49) | fn test_12() {
  function test_13 (line 53) | fn test_13() {
  function test_14 (line 57) | fn test_14() {
  function test_15 (line 61) | fn test_15() {
  function test_16 (line 65) | fn test_16() {
  function test_17 (line 69) | fn test_17() {
  function test_18 (line 73) | fn test_18() {
  function test_19 (line 77) | fn test_19() {
  function test_20 (line 81) | fn test_20() {
  function test_21 (line 85) | fn test_21() {
  function test_22 (line 89) | fn test_22() {
  function test_23 (line 93) | fn test_23() {
  function test_24 (line 97) | fn test_24() {
  function execute_testcase (line 101) | fn execute_testcase(filename: &str) {
  function decode_hex (line 111) | fn decode_hex(hex: &str) -> Vec<u8> {

FILE: src/windows_native/descriptor/typedefs.rs
  type Usage (line 19) | pub type Usage = u16;
  type LinkCollectionNode (line 24) | pub struct LinkCollectionNode {
    method is_alias (line 35) | pub fn is_alias(&self) -> bool {
    method collection_type (line 38) | pub fn collection_type(&self) -> u8 {
  type CapsInfo (line 46) | pub struct CapsInfo {
  type UnknownToken (line 56) | pub struct UnknownToken {
  type Button (line 64) | pub struct Button {
  type NotButton (line 71) | pub struct NotButton {
  type Range (line 89) | pub struct Range {
  type NotRange (line 102) | pub struct NotRange {
  type Caps (line 123) | pub struct Caps {
    method is_button_cap (line 146) | pub fn is_button_cap(&self) -> bool {
    method is_range (line 149) | pub fn is_range(&self) -> bool {
    method is_alias (line 152) | pub fn is_alias(&self) -> bool {
    method is_string_range (line 155) | pub fn is_string_range(&self) -> bool {
    method is_designator_range (line 158) | pub fn is_designator_range(&self) -> bool {
    method range (line 162) | pub fn range(&self) -> Range {
    method not_range (line 167) | pub fn not_range(&self) -> NotRange {
    method button (line 172) | pub fn button(&self) -> Button {
    method not_button (line 177) | pub fn not_button(&self) -> NotButton {
    method get_bit_range (line 182) | pub fn get_bit_range(&self) -> BitRange {
  type HidpPreparsedData (line 194) | pub struct HidpPreparsedData {

FILE: src/windows_native/descriptor/types.rs
  type ReportType (line 7) | pub enum ReportType {
    method values (line 13) | pub const fn values() -> impl IntoIterator<Item = Self> {
    type Error (line 74) | type Error = ();
    method try_from (line 76) | fn try_from(value: MainItems) -> Result<Self, Self::Error> {
  type Items (line 20) | pub enum Items {
  type MainItems (line 52) | pub enum MainItems {
    method from (line 64) | fn from(value: ReportType) -> Self {
  type BitRange (line 87) | pub struct BitRange {
    method merge (line 93) | pub fn merge(self, other: BitRange) -> BitRange {
  type ItemNodeType (line 102) | pub enum ItemNodeType {
  type MainItemNode (line 109) | pub struct MainItemNode {
    method new (line 120) | pub fn new(

FILE: src/windows_native/dev_node.rs
  type DevNode (line 12) | pub struct DevNode(u32);
    method from_device_id (line 15) | pub fn from_device_id(device_id: &U16Str) -> WinResult<Self> {
    method parent (line 23) | pub fn parent(self) -> WinResult<Self> {
    method get_property_size (line 30) | fn get_property_size<T: DeviceProperty>(
    method get_property (line 54) | pub fn get_property<T: DeviceProperty>(self, property_key: impl Proper...

FILE: src/windows_native/device_info.rs
  constant PKEY_DeviceInterface_Bluetooth_DeviceAddress (line 23) | const PKEY_DeviceInterface_Bluetooth_DeviceAddress: PROPERTYKEY = PROPER...
  constant PKEY_DeviceInterface_Bluetooth_Manufacturer (line 28) | const PKEY_DeviceInterface_Bluetooth_Manufacturer: PROPERTYKEY = PROPERT...
  constant PKEY_DeviceInterface_Bluetooth_ModelNumber (line 33) | const PKEY_DeviceInterface_Bluetooth_ModelNumber: PROPERTYKEY = PROPERTY...
  function read_string (line 38) | fn read_string(
  function get_device_info (line 61) | pub fn get_device_info(path: &U16Str, handle: &Handle) -> DeviceInfo {
  function get_internal_info (line 85) | fn get_internal_info(interface_path: &U16Str, dev: &mut DeviceInfo) -> W...
  function get_usb_info (line 125) | fn get_usb_info(dev: &mut DeviceInfo, mut dev_node: DevNode) -> WinResul...
  function get_ble_info (line 202) | fn get_ble_info(dev: &mut DeviceInfo, dev_node: DevNode) -> WinResult<()> {
  function extract_int_token_value (line 236) | fn extract_int_token_value(u16str: &U16Str, token: &str) -> Option<u32> {

FILE: src/windows_native/error.rs
  type WinResult (line 5) | pub type WinResult<T> = Result<T, WinError>;
  type WinError (line 8) | pub enum WinError {
    method last (line 22) | pub fn last() -> Self {
    method from (line 90) | fn from(value: Win32Error) -> Self {
  method from (line 28) | fn from(value: WinError) -> Self {
  function config_to_error (line 40) | fn config_to_error(ret: CONFIGRET) -> WinError {
  function check_config (line 50) | pub fn check_config(ret: CONFIGRET, expected: CONFIGRET) -> WinResult<()> {
  function check_boolean (line 58) | pub fn check_boolean(ret: bool) -> WinResult<()> {
  type Win32Error (line 67) | pub enum Win32Error {
    method last (line 75) | pub fn last() -> Self {
  method from (line 99) | fn from(value: Win32Error) -> Self {

FILE: src/windows_native/hid.rs
  function get_interface_guid (line 11) | pub fn get_interface_guid() -> GUID {
  function get_hid_attributes (line 19) | pub fn get_hid_attributes(handle: &Handle) -> HIDD_ATTRIBUTES {
  type PreparsedData (line 31) | pub struct PreparsedData(isize);
    method load (line 42) | pub fn load(handle: &Handle) -> WinResult<Self> {
    method as_ptr (line 50) | pub fn as_ptr(&self) -> *const c_void {
    method get_caps (line 54) | pub fn get_caps(&self) -> WinResult<HIDP_CAPS> {
  method drop (line 34) | fn drop(&mut self) {

FILE: src/windows_native/interfaces.rs
  type Interface (line 13) | pub struct Interface;
    method get_property_size (line 16) | fn get_property_size<T: DeviceProperty>(
    method get_property (line 40) | pub fn get_property<T: DeviceProperty>(
    method get_interface_list_length (line 64) | fn get_interface_list_length(interface: GUID) -> WinResult<usize> {
    method get_interface_list (line 78) | pub fn get_interface_list() -> WinResult<U16StringList> {

FILE: src/windows_native/mod.rs
  constant STRING_BUF_LEN (line 51) | const STRING_BUF_LEN: usize = 128;
  type HidApiBackend (line 53) | pub struct HidApiBackend;
    method get_hid_device_info_vector (line 55) | pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec...
    method open (line 59) | pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
    method open_serial (line 63) | pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevic...
    method open_path (line 67) | pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
  type HidDevice (line 73) | pub struct HidDevice {
  type AsyncState (line 85) | struct AsyncState {
    method new (line 91) | fn new(report_size: usize) -> Self {
    method clear_buffer (line 98) | fn clear_buffer(&mut self) {
    method fill_buffer (line 102) | fn fill_buffer(&mut self, data: &[u8]) {
    method buffer_len (line 116) | fn buffer_len(&self) -> usize {
    method buffer_ptr (line 120) | fn buffer_ptr(&mut self) -> *mut u8 {
  method fmt (line 126) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method write (line 132) | fn write(&self, data: &[u8]) -> HidResult<usize> {
  method read (line 158) | fn read(&self, buf: &mut [u8]) -> HidResult<usize> {
  method read_timeout (line 162) | fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize> {
  method send_feature_report (line 228) | fn send_feature_report(&self, data: &[u8]) -> HidResult<()> {
  method get_feature_report (line 247) | fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
  method send_output_report (line 281) | fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
  method set_blocking_mode (line 297) | fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
  method get_manufacturer_string (line 302) | fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
  method get_product_string (line 306) | fn get_product_string(&self) -> HidResult<Option<String>> {
  method get_serial_number_string (line 310) | fn get_serial_number_string(&self) -> HidResult<Option<String>> {
  method get_indexed_string (line 314) | fn get_indexed_string(&self, index: i32) -> HidResult<Option<String>> {
  method get_device_info (line 328) | fn get_device_info(&self) -> HidResult<DeviceInfo> {
  method get_report_descriptor (line 332) | fn get_report_descriptor(&self, buf: &mut [u8]) -> HidResult<usize> {
  method get_container_id (line 341) | fn get_container_id(&self) -> HidResult<GUID> {
  method drop (line 354) | fn drop(&mut self) {
  function enumerate_devices (line 370) | fn enumerate_devices(vendor_id: u16, product_id: u16) -> WinResult<Vec<D...
  function open_device (line 383) | fn open_device(path: &U16Str, open_rw: bool) -> WinResult<Handle> {
  function open (line 405) | fn open(vid: u16, pid: u16, sn: Option<&str>) -> HidResult<HidDevice> {
  function open_path (line 414) | fn open_path(device_path: &CStr) -> HidResult<HidDevice> {

FILE: src/windows_native/string.rs
  type U16Str (line 15) | pub struct U16Str([u16]);
    method from_slice_unsafe (line 18) | unsafe fn from_slice_unsafe(slice: &[u16]) -> &Self {
    method from_slice_mut_unsafe (line 23) | unsafe fn from_slice_mut_unsafe(slice: &mut [u16]) -> &mut Self {
    method from_slice (line 28) | pub fn from_slice(slice: &[u16]) -> &Self {
    method from_slice_mut (line 41) | pub fn from_slice_mut(slice: &mut [u16]) -> &mut Self {
    method from_slice_list (line 54) | pub fn from_slice_list(slice: &[u16]) -> impl Iterator<Item = &U16Str> {
    method from_slice_list_mut (line 58) | pub fn from_slice_list_mut(slice: &mut [u16]) -> impl Iterator<Item = ...
    method as_ptr (line 62) | pub fn as_ptr(&self) -> PCWSTR {
    method as_slice (line 66) | pub fn as_slice(&self) -> &[u16] {
    method as_slice_mut (line 69) | pub fn as_slice_mut(&mut self) -> &mut [u16] {
    method make_uppercase_ascii (line 74) | pub fn make_uppercase_ascii(&mut self) {
    method starts_with_ignore_case (line 82) | pub fn starts_with_ignore_case(&self, pattern: &str) -> bool {
    method find_index (line 89) | pub fn find_index(&self, pattern: &str) -> Option<usize> {
  method to_string (line 105) | fn to_string(&self) -> String {
  method from (line 111) | fn from(value: &U16Str) -> Self {
  method fmt (line 119) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  type U16String (line 127) | pub struct U16String(Vec<u16>);
    type Error (line 150) | type Error = Utf8Error;
    method try_from (line 152) | fn try_from(value: &CStr) -> Result<Self, Self::Error> {
  method fmt (line 130) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  type Target (line 136) | type Target = U16Str;
  method deref (line 138) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 144) | fn deref_mut(&mut self) -> &mut Self::Target {
  method from (line 160) | fn from(value: U16String) -> Self {
  constant TYPE (line 166) | const TYPE: DEVPROPTYPE = DEVPROP_TYPE_STRING;
  method create_sized (line 168) | fn create_sized(bytes: usize) -> Self {
  method as_ptr_mut (line 173) | fn as_ptr_mut(&mut self) -> *mut u8 {
  method validate (line 177) | fn validate(&self) {
  type U16StringList (line 190) | pub struct U16StringList(pub Vec<u16>);
    method iter (line 213) | pub fn iter(&self) -> impl Iterator<Item = &U16Str> {
    method iter_mut (line 216) | pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut U16Str> {
  constant TYPE (line 193) | const TYPE: DEVPROPTYPE = DEVPROP_TYPE_STRING_LIST;
  method create_sized (line 195) | fn create_sized(bytes: usize) -> Self {
  method as_ptr_mut (line 200) | fn as_ptr_mut(&mut self) -> *mut u8 {
  method validate (line 204) | fn validate(&self) {
  function is_null (line 221) | fn is_null(c: &u16) -> bool {

FILE: src/windows_native/types.rs
  type DeviceProperty (line 14) | pub unsafe trait DeviceProperty {
    constant TYPE (line 15) | const TYPE: DEVPROPTYPE;
    method create_sized (line 16) | fn create_sized(bytes: usize) -> Self;
    method as_ptr_mut (line 17) | fn as_ptr_mut(&mut self) -> *mut u8;
    method validate (line 18) | fn validate(&self) {}
    constant TYPE (line 22) | const TYPE: DEVPROPTYPE = DEVPROP_TYPE_GUID;
    method create_sized (line 24) | fn create_sized(bytes: usize) -> Self {
    method as_ptr_mut (line 29) | fn as_ptr_mut(&mut self) -> *mut u8 {
  type PropertyKey (line 34) | pub trait PropertyKey: Copy {
    method as_ptr (line 35) | fn as_ptr(&self) -> *const DEVPROPKEY;
    method as_ptr (line 39) | fn as_ptr(&self) -> *const DEVPROPKEY {
    method as_ptr (line 45) | fn as_ptr(&self) -> *const DEVPROPKEY {
  type InternalBusType (line 51) | pub enum InternalBusType {
  method from (line 61) | fn from(value: InternalBusType) -> Self {
  type Handle (line 73) | pub struct Handle(HANDLE);
    method from_raw (line 76) | pub fn from_raw(handle: HANDLE) -> Self {
    method as_raw (line 79) | pub fn as_raw(&self) -> HANDLE {
  method drop (line 85) | fn drop(&mut self) {
  type Overlapped (line 95) | pub struct Overlapped(OVERLAPPED);
    method event_handle (line 98) | pub fn event_handle(&self) -> HANDLE {
    method as_raw (line 101) | pub fn as_raw(&mut self) -> *mut OVERLAPPED {
    method get_result (line 105) | pub fn get_result(&mut self, handle: &Handle, timeout: Option<u32>) ->...
  method default (line 124) | fn default() -> Self {
  method drop (line 136) | fn drop(&mut self) {

FILE: src/windows_native/utils.rs
  type PeakIterExt (line 1) | pub trait PeakIterExt<T: Iterator> {
    method peaking (line 2) | fn peaking(self) -> PeakingIter<T>;
  method peaking (line 6) | fn peaking(mut self) -> PeakingIter<T> {
  type PeakingIter (line 14) | pub struct PeakingIter<T: Iterator> {
  type Item (line 20) | type Item = (I::Item, Option<I::Item>);
  method next (line 22) | fn next(&mut self) -> Option<Self::Item> {
Condensed preview — 89 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (466K chars).
[
  {
    "path": ".github/workflows/rust.yml",
    "chars": 6100,
    "preview": "name: Rust\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n  workflow_dispatch:\n\nenv:"
  },
  {
    "path": ".gitignore",
    "chars": 56,
    "preview": "target\nCargo.lock\n.idea/\nhidapi-rs.iml\n.helix/\n.vscode/\n"
  },
  {
    "path": ".gitmodules",
    "chars": 88,
    "preview": "[submodule \"etc/hidapi\"]\n\tpath = etc/hidapi\n\turl = https://github.com/libusb/hidapi.git\n"
  },
  {
    "path": ".travis.yml",
    "chars": 669,
    "preview": "language: rust\n\n\nmatrix:\n  include:\n    - env: TARGET=x86_64-unknown-linux-gnu FEATURE_FLAGS=\"linux-static-libusb\"\n\n    "
  },
  {
    "path": "Cargo.toml",
    "chars": 2308,
    "preview": "[package]\nname = \"hidapi\"\nversion = \"2.6.5\"\nauthors = [\n    \"Roland Ruckerbauer <mail@ruabmbua.dev>\",\n    \"Osspial <ossp"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1064,
    "preview": "Copyright 2017 The hidapi-rs Developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of"
  },
  {
    "path": "README.md",
    "chars": 1599,
    "preview": "# hidapi [![Version](https://img.shields.io/crates/v/hidapi.svg)](https://crates.io/crates/hidapi) [![License: MIT](http"
  },
  {
    "path": "build.rs",
    "chars": 7055,
    "preview": "// **************************************************************************\n// Copyright (c) 2015 Roland Ruckerbauer A"
  },
  {
    "path": "examples/co2mon.rs",
    "chars": 3832,
    "preview": "/****************************************************************************\n    Copyright (c) 2015 Artyom Pavlov All R"
  },
  {
    "path": "examples/dump_descriptors.rs",
    "chars": 904,
    "preview": "use hidapi::HidApi;\n\nfn main() {\n    println!(\"Printing all available hid devices:\");\n\n    match HidApi::new() {\n       "
  },
  {
    "path": "examples/logitech_gprox.rs",
    "chars": 728,
    "preview": "/****************************************************************************\n    Copyright (c) 2022 ruabmbua All Rights"
  },
  {
    "path": "examples/lshid.rs",
    "chars": 1047,
    "preview": "/****************************************************************************\n    Copyright (c) 2015 Osspial All Rights "
  },
  {
    "path": "examples/open_first_device.rs",
    "chars": 1199,
    "preview": "/****************************************************************************\n    Copyright (c) 2018 Roland Ruckerbauer "
  },
  {
    "path": "examples/readhid.rs",
    "chars": 986,
    "preview": "/****************************************************************************\n    Copyright (c) 2015 Osspial All Rights "
  },
  {
    "path": "examples/static_lifetime_bound.rs",
    "chars": 1265,
    "preview": "/****************************************************************************\nCopyright (c) 2015 Osspial All Rights Rese"
  },
  {
    "path": "src/error.rs",
    "chars": 2585,
    "preview": "// **************************************************************************\n// Copyright (c) 2018 Roland Ruckerbauer A"
  },
  {
    "path": "src/ffi.rs",
    "chars": 4486,
    "preview": "#![allow(unused_imports, dead_code)]\n\n/// **************************************************************************\n///"
  },
  {
    "path": "src/hidapi/macos.rs",
    "chars": 976,
    "preview": "//! The extra beahviour for macOS\n\nuse super::HidDevice;\nuse crate::{ffi, HidDeviceBackendBase, HidDeviceBackendMacos, H"
  },
  {
    "path": "src/hidapi/windows.rs",
    "chars": 713,
    "preview": "//! The extra behaviour for Windows\n\nuse std::ptr::addr_of_mut;\nuse windows_sys::core::GUID;\n\nuse super::HidDevice;\nuse "
  },
  {
    "path": "src/hidapi.rs",
    "chars": 11192,
    "preview": "//! The implementation which uses the C library to perform operations\n\nuse std::{\n    ffi::CStr,\n    fmt::{self, Debug},"
  },
  {
    "path": "src/lib.rs",
    "chars": 25795,
    "preview": "// **************************************************************************\n// Copyright (c) 2015 Osspial All Rights R"
  },
  {
    "path": "src/linux_native/ioctl.rs",
    "chars": 850,
    "preview": "//! The IOCTL calls we need for the native linux backend\n\nuse nix::{ioctl_read, ioctl_read_buf, ioctl_write_buf};\n\n// Fr"
  },
  {
    "path": "src/linux_native.rs",
    "chars": 22830,
    "preview": "//! This backend uses libudev to discover devices and then talks to hidraw directly\n\nmod ioctl;\n\n#[cfg(feature = \"linux-"
  },
  {
    "path": "src/macos.rs",
    "chars": 1207,
    "preview": "use libc::c_int;\n\nuse crate::ffi;\nuse crate::{HidApi, HidDevice, HidResult};\n\nimpl HidApi {\n    /// Changes the behavior"
  },
  {
    "path": "src/windows.rs",
    "chars": 438,
    "preview": "use crate::{HidDevice, HidResult};\npub use windows_sys::core::GUID;\n\nimpl HidDevice {\n    /// Get the container ID for a"
  },
  {
    "path": "src/windows_native/descriptor/encoder.rs",
    "chars": 23364,
    "preview": "use crate::windows_native::descriptor::typedefs::{Caps, LinkCollectionNode};\nuse crate::windows_native::descriptor::type"
  },
  {
    "path": "src/windows_native/descriptor/mod.rs",
    "chars": 28523,
    "preview": "mod encoder;\n#[cfg(test)]\nmod tests;\nmod typedefs;\nmod types;\n\nuse crate::windows_native::descriptor::encoder::encode_de"
  },
  {
    "path": "src/windows_native/descriptor/tests.rs",
    "chars": 2683,
    "preview": "use crate::windows_native::descriptor::get_descriptor_ptr;\nuse std::fs::read_to_string;\n\n#[test]\nfn test_01() {\n    exec"
  },
  {
    "path": "src/windows_native/descriptor/typedefs.rs",
    "chars": 4808,
    "preview": "use crate::windows_native::descriptor::types::BitRange;\nuse std::mem::size_of;\n\n// Reverse engineered typedefs for the i"
  },
  {
    "path": "src/windows_native/descriptor/types.rs",
    "chars": 3811,
    "preview": "#![allow(dead_code)]\n\nuse std::fmt::Debug;\n\n#[derive(Copy, Clone, Eq, PartialEq, Hash)]\n#[repr(u16)]\npub enum ReportType"
  },
  {
    "path": "src/windows_native/dev_node.rs",
    "chars": 2358,
    "preview": "use crate::windows_native::error::{check_config, WinError, WinResult};\nuse crate::windows_native::string::U16Str;\nuse cr"
  },
  {
    "path": "src/windows_native/device_info.rs",
    "chars": 10022,
    "preview": "use crate::windows_native::dev_node::DevNode;\nuse crate::windows_native::error::WinResult;\nuse crate::windows_native::hi"
  },
  {
    "path": "src/windows_native/error.rs",
    "chars": 2501,
    "preview": "use crate::HidError;\nuse windows_sys::Win32::Devices::DeviceAndDriverInstallation::*;\nuse windows_sys::Win32::Foundation"
  },
  {
    "path": "src/windows_native/hid.rs",
    "chars": 1755,
    "preview": "use crate::windows_native::error::{check_boolean, WinError, WinResult};\nuse crate::windows_native::types::Handle;\nuse st"
  },
  {
    "path": "src/windows_native/interfaces.rs",
    "chars": 3285,
    "preview": "use crate::windows_native::error::{check_config, WinError, WinResult};\nuse crate::windows_native::hid::get_interface_gui"
  },
  {
    "path": "src/windows_native/mod.rs",
    "chars": 14863,
    "preview": "//! The implementation which uses the the raw win32 api to perform operations\n\nmacro_rules! ensure {\n    ($cond:expr, $r"
  },
  {
    "path": "src/windows_native/string.rs",
    "chars": 5990,
    "preview": "use crate::windows_native::types::DeviceProperty;\nuse crate::WcharString;\nuse std::ffi::CStr;\nuse std::fmt::{Debug, Form"
  },
  {
    "path": "src/windows_native/types.rs",
    "chars": 3581,
    "preview": "use crate::windows_native::error::{WinError, WinResult};\nuse crate::BusType;\nuse std::mem::{size_of, zeroed};\nuse std::p"
  },
  {
    "path": "src/windows_native/utils.rs",
    "chars": 631,
    "preview": "pub trait PeakIterExt<T: Iterator> {\n    fn peaking(self) -> PeakingIter<T>;\n}\n\nimpl<T: Iterator> PeakIterExt<T> for T {"
  },
  {
    "path": "tests/pp_data/045E_02FF_0005_0001.expected",
    "chars": 677,
    "preview": "0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x09, 0x00, 0xA1, 0x00, \n0x09, 0x30, 0x09, 0x31, 0x15, 0x00, 0x25, 0xFF, 0x35, 0x00,"
  },
  {
    "path": "tests/pp_data/045E_02FF_0005_0001.pp_data",
    "chars": 5015,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046A_0011_0006_0001.expected",
    "chars": 402,
    "preview": "0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, \n0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,"
  },
  {
    "path": "tests/pp_data/046A_0011_0006_0001.pp_data",
    "chars": 2231,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_0A37_0001_000C.expected",
    "chars": 939,
    "preview": "0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x01, 0x09, 0xE9, \n0x09, 0xEA, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x02,"
  },
  {
    "path": "tests/pp_data/046D_0A37_0001_000C.pp_data",
    "chars": 7943,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_B010_0001_000C.expected",
    "chars": 140,
    "preview": "0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x05, 0x06, \n0x09, 0x20, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x01,"
  },
  {
    "path": "tests/pp_data/046D_B010_0001_000C.pp_data",
    "chars": 983,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_B010_0001_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09, \n0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,"
  },
  {
    "path": "tests/pp_data/046D_B010_0001_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_B010_0002_0001.expected",
    "chars": 457,
    "preview": "0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, \n0x85, 0x02, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, 0x00,"
  },
  {
    "path": "tests/pp_data/046D_B010_0002_0001.pp_data",
    "chars": 3575,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_B010_0002_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09, \n0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,"
  },
  {
    "path": "tests/pp_data/046D_B010_0002_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_B010_0006_0001.expected",
    "chars": 414,
    "preview": "0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x85, 0x04, 0x05, 0x07, \n0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,"
  },
  {
    "path": "tests/pp_data/046D_B010_0006_0001.pp_data",
    "chars": 2231,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C077_0002_0001.expected",
    "chars": 280,
    "preview": "0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, \n0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01,"
  },
  {
    "path": "tests/pp_data/046D_C077_0002_0001.pp_data",
    "chars": 2951,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C283_0004_0001.expected",
    "chars": 1043,
    "preview": "0x05, 0x01, 0x09, 0x04, 0xA1, 0x01, 0x09, 0x00, 0xA1, 0x02, \n0x09, 0x01, 0xA1, 0x00, 0x09, 0x30, 0x09, 0x31, 0x15, 0x00,"
  },
  {
    "path": "tests/pp_data/046D_C283_0004_0001.pp_data",
    "chars": 6263,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C52F_0001_000C.expected",
    "chars": 152,
    "preview": "0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x19, 0x01, \n0x2A, 0x8C, 0x02, 0x15, 0x01, 0x26, 0x8C, 0x02, 0x75, 0x10,"
  },
  {
    "path": "tests/pp_data/046D_C52F_0001_000C.pp_data",
    "chars": 983,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C52F_0001_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09, \n0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,"
  },
  {
    "path": "tests/pp_data/046D_C52F_0001_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C52F_0002_0001.expected",
    "chars": 445,
    "preview": "0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, \n0x05, 0x09, 0x19, 0x01, 0x29, 0x10, 0x15, 0x00, 0x25, 0x01,"
  },
  {
    "path": "tests/pp_data/046D_C52F_0002_0001.pp_data",
    "chars": 3575,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C52F_0002_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09, \n0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,"
  },
  {
    "path": "tests/pp_data/046D_C52F_0002_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0001_000C.expected",
    "chars": 152,
    "preview": "0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x19, 0x01, \n0x2A, 0x8C, 0x02, 0x15, 0x01, 0x26, 0x8C, 0x02, 0x75, 0x10,"
  },
  {
    "path": "tests/pp_data/046D_C534_0001_000C.pp_data",
    "chars": 983,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0001_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09, \n0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,"
  },
  {
    "path": "tests/pp_data/046D_C534_0001_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0002_0001.expected",
    "chars": 457,
    "preview": "0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, \n0x85, 0x02, 0x05, 0x09, 0x19, 0x01, 0x29, 0x10, 0x15, 0x00,"
  },
  {
    "path": "tests/pp_data/046D_C534_0002_0001.pp_data",
    "chars": 3575,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0002_FF00.expected",
    "chars": 219,
    "preview": "0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09, \n0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,"
  },
  {
    "path": "tests/pp_data/046D_C534_0002_FF00.pp_data",
    "chars": 1607,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x02, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0006_0001.expected",
    "chars": 402,
    "preview": "0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, \n0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,"
  },
  {
    "path": "tests/pp_data/046D_C534_0006_0001.pp_data",
    "chars": 2231,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,\n"
  },
  {
    "path": "tests/pp_data/046D_C534_0080_0001.expected",
    "chars": 189,
    "preview": "0x05, 0x01, 0x09, 0x80, 0xA1, 0x01, 0x85, 0x04, 0x09, 0x82, \n0x09, 0x81, 0x09, 0x83, 0x15, 0x01, 0x25, 0x03, 0x75, 0x02,"
  },
  {
    "path": "tests/pp_data/046D_C534_0080_0001.pp_data",
    "chars": 2231,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00,\n"
  },
  {
    "path": "tests/pp_data/047F_C056_0001_000C.expected",
    "chars": 603,
    "preview": "0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x01, 0x09, 0xE9, \n0x09, 0xEA, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x02,"
  },
  {
    "path": "tests/pp_data/047F_C056_0001_000C.pp_data",
    "chars": 4727,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,\n"
  },
  {
    "path": "tests/pp_data/047F_C056_0003_FFA0.expected",
    "chars": 1433,
    "preview": "0x06, 0xA0, 0xFF, 0x09, 0x03, 0xA1, 0x01, 0x85, 0x03, 0x09, \n0x30, 0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x20, 0x81,"
  },
  {
    "path": "tests/pp_data/047F_C056_0003_FFA0.pp_data",
    "chars": 16583,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x03, 0x00, 0xA0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,\n"
  },
  {
    "path": "tests/pp_data/047F_C056_0005_000B.expected",
    "chars": 994,
    "preview": "0x05, 0x0B, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x08, 0x09, 0x2F, \n0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x06,"
  },
  {
    "path": "tests/pp_data/047F_C056_0005_000B.pp_data",
    "chars": 5975,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,\n"
  },
  {
    "path": "tests/pp_data/17CC_1130_0000_FF01.expected",
    "chars": 4550,
    "preview": "0x06, 0x01, 0xFF, 0x09, 0x00, 0xA1, 0x01, 0x09, 0x01, 0xA1, \n0x02, 0x85, 0x01, 0x09, 0x03, 0x09, 0x03, 0x09, 0x03, 0x09,"
  },
  {
    "path": "tests/pp_data/17CC_1130_0000_FF01.pp_data",
    "chars": 146603,
    "preview": "0x48, 0x69, 0x64, 0x50, 0x20, 0x4B, 0x44, 0x52, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00,\n"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the ruabmbua/hidapi-rs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 89 files (440.9 KB), approximately 253.8k tokens, and a symbol index with 432 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!