Git Product home page Git Product logo

usbd-hid's People

Contributors

9names avatar adminxvii avatar antonok-edm avatar gentoli avatar haata avatar jacobrosenthal avatar jkristell avatar jyelloz avatar n8tlarsen avatar nashenas88 avatar riskable avatar rmsyn avatar twitchyliquid64 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

usbd-hid's Issues

Bump version

I'd like to push some crates that depend on newer features (get/set protocol).

MouseReport doesn't work with scroll wheel buttons

I tried adding a 4th (1 << 3) and 5th (1 << 4) button to my MouseReport but that doesn't work. Apparently mouse wheel events (which appear on the host OS as buttons 4 and 5) are special somehow. Likely the gen_hid_descriptor call in descriptor.rs needs adjusting but I don't know enough to make adjustments on my own.

Is there a way to change how I'm using this crate so that I can send wheel events?

packed_bits in downstream consumers isnt documented

if someone else designs the report, like mousereport, you dont actually know that buttons are supposed to be packed_bits, nor do you know you're hardcoded to only have 3 buttons

This could simply be documented, but seems brittle.

Win10 compatibility.

There seems to be some compatibility issue with Win10 and the generated descriptor(s).

#[gen_hid_descriptor(
    (collection = APPLICATION, usage = 0x01, usage_page = 0xff00) = {
        #[item_settings data] data=input;
    }
)]

#[allow(dead_code)]
struct Data {
    data: u8,
}

The generated descriptor looks like this.

data [
    0x6, 0x0, 0xff,      // USEGE PAGE Vendor
    0x9, 0x1,             // USAGE Vendor
    0xa1, 0x1,           // Collection (Application)
    0x15, 0x0,          // Logical minimum
    0x26, 0xff, 0x0,   // Logical maximum
    0x75, 0x8,          // Report size 8
    0x95, 0x1,          // Report count 1
    0x81, 0x0,          // Input
    0xc0,                 // End application collection
]

This works well under Linux (tested under arch) and OSX. Hower under Win10 the device is not properly detected/configured (USB Device Viewer shows the descriptors correctly but claims the devic to be in low power state.

Adding a usage before Input solves the issue.

data [
    0x6, 0x0, 0xff,      // USEGE PAGE Vendor
    0x9, 0x1,             // USAGE Vendor
    0xa1, 0x1,           // Collection (Application)
    0x15, 0x0,          // Logical minimum
    0x26, 0xff, 0x0,   // Logical maximum
    0x75, 0x8,          // Report size 8
    0x95, 0x1,          // Report count 1
    0x09, 0x01,        // USAGE Vendor <---- manually added here
    0x81, 0x0,          // Input
    0xc0,                 // End application collection
]

Either MS is out of specs (the wrapping usage should be inherited) or Linux/OSX just playing nice guessing, not sure.

Maybe there is already some workaround for this problem that I missed. If not, perhaps a syntax extension to the #[item_settings usage = 0x01, ...] would be possible. Or maybe let the proc macro always emit inherited usage from the wrapping collection (in this case).

Admittedly I'm a USB novice, so not sure what's best here.
/Per

mouse example panics on macosx atsamd51

Technically I ran it on atsamd51, so not exactly your example, but figure better to discuss here.

the push_mouse_movement unwrap specifically

you can avoid panic

        let _ = push_mouse_movement(MouseReport { buttons: 0, x, y });

Still doesnt appear as a mouse, but then logging then shows

UsbBus::write 3 bytes [0, 0, 5] to ep EndpointAddress(129) -> BUSY trcpt1=false
UsbBus::write 3 bytes [0, 0, 251] to ep EndpointAddress(129) -> BUSY trcpt1=false

Maybe if I can get it to recognize better itll read and not be busy? not sure. Just wanted to track somewhere

Can we get a 0.6.1 release?

My byteorder PR (#46) that was just merged corrects a big conflict with embedded-graphics and it'd be nice to not have to put:

usbd-hid = { git = "https://github.com/twitchyliquid64/usbd-hid.git", branch = "master" }

...in my Cargo.toml.

That is all ๐Ÿ˜„

Add keyboard example

Hi,
can you add a keyboard example? I want to build a mechanical keyboard with an ARM controller running Rust instead of C

and merry christmas

Warning when using `packed_bits` on a field wider than u8.

I'm trying to create a descriptor for a joystick with 10 buttons:

#[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = JOYSTICK) = {
        (usage = X,) = {x=input;};
        (usage = Y,) = {y=input;};
        (usage_page = BUTTON, usage_min = BUTTON_1, usage_max = 10) = {
            #[packed_bits 10] #[item_settings data,variable,absolute] buttons=input;
        };
    }
)]
struct InputReport {
    x: u8,
    y: u8,
    buttons: u16,
}

This yields the following warning:

warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
  --> targets\stm32f072-disco\src\main.rs:23:1
   |
23 | / #[gen_hid_descriptor(
24 | |     (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = JOYSTICK) = {
25 | |         (usage = X,) = {x=input;};
26 | |         (usage = Y,) = {y=input;};
...  |
30 | |     }
31 | | )]
   | |__^
   |
   = note: `#[warn(safe_packed_borrows)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
   = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior
   = note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

Is there an issue with the generated keyboard descriptor?

Hi, I'm building a keyboard and am currently using this crate. I'm currently using it on MacOS.

I noticed I'm receiving phantom key inputs when I hold down 4 or more keys. Normally this would sound like ghosting, but my keyboard has diodes on every switch, and I verified with probe-run and defmt that I'm not adding these characters to the KeyboardReport struct.

  • When 3 keys are held down and a fourth is pressed
    • The letter a gets inserted after the fourth press
  • When 4 keys are held down and a fifth is pressed
    • The letter b gets inserted after the fifth press
  • When 5 keys are held down and a sixth is pressed
    • The letter c gets inserted after the sixth press

For example:

  • Held keys: q, w, e
    • Pressing r results in qwera instead of qwer
  • Held keys: q, w, e, r
    • Pressing t results in qweratb instead of qwert
  • Held keys: q, w, e, r, t
    • Pressing y results in qweratbyc instead of qwerty

I eventually narrowed this down to the keyboard HID descriptor. I swapped it out for one from another project, and now the keyboard works properly with the scenarios above:

bschwind/key-ripper#10

I don't yet know enough about USB HID descriptors to say what's going on, but it feels like a byte is out of place somewhere.

Example SystemControlReport code

Hi Guys

I would like to write a small application using SystemControlReport's to put a PC to sleep or wake it up. Is there any example code that shows how this could be done?

My initial attempts to use SystemControlReport's does not seem to do anything (tested on Mac, Windows 10 and Linux!)

The base code has been modified from the twitching usb mouse example (which is working fine).

pseudo code is:

let usb_hid = HIDClass::new(bus_ref, SystemControlReport::desc(), 60);

let usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27da))
    .manufacturer("My_Comany")
    .product("My_Product")
    .serial_number("0000")
    .device_class(0)
    .build();

let report = SystemControlReport {
    usage_id: SystemControlKey::Sleep.into()
}

critical_section::with(|_| unsafe {
    USB_HID.as_mut().map(|hid| hid.push_input(&report))
}).unwrap()

The sending of the sleep is triggered by a GPIO pin, and I can see that the report is being sent, with the return value being 1, which I believe means that one byte was successfully sent (which is the size of the SystemControlKey event.

Any ideas? Or any pointers to functional example code?

Incompatibility with syn 1.0.76 ?

Hi, I recently ran into a problem using this library where the MediaKeyboardReport.desc() was expanding to:

[
  5u8, 12u8, 9u8, 1u8, 161u8, 1u8, 5u8, 12u8, 25u8, 0u8, 42u8, 20u8, 5u8, 21u8, 0u8,
  39u8, 255u8, 255u8, 0u8, 0u8, 117u8, 16u8, 149u8, 1u8, 129u8, 2u8, 192u8,
]

The expected value is:

[
  5u8, 12u8, 9u8, 1u8, 161u8, 1u8, 5u8, 12u8, 25u8, 0u8, 42u8, 20u8, 5u8, 21u8, 0u8,
  39u8, 255u8, 255u8, 0u8, 0u8, 117u8, 16u8, 149u8, 1u8, 129u8, 0u8, 192u8,
]

... with the second-to-last byte differing. Specifically, the data type flag is being set to variable when the declaration says it's an array. I tracked it down to dtolnay/syn@c09a2e5 ` but I am not very experienced with proc macros so I am not sure if this is a new bug in syn or if the HID macros were just relying on an old bug in syn that was fixed.

Add support for mediakeys

Hi

After realizing that the KeyboardReport class doesn't support media keys I decided to try to implement it for a little demo of a infrared receiver controlling the media player on my PC.

This my first ever encounter with USB and USB descriptors and based on googling and testing until it worked.

My first attempt was to add a new MediaKeyboardReport #9

Then I decided to just add it to the KeyboardReport, and that seems to work as well. #10

Both branches tested on Linux, and Linux only.

The demo can be found here:

https://github.com/jkristell/infrared/blob/cmd/examples/stm32f103-examples/examples/kbd.rs

If you think this is good enough for adding we should probably add an enum with the keycodes for the Media keys as well.

Compile errors from ssmarshal dependency

Hi - apologies in advance if this is a really stupid question - I'm just starting to explore rust ๐Ÿ˜…

I'm trying to use your library for the hid descriptor macros (great work btw!) but I'm getting some compiler errors (using cargo build) while it's trying to compile ssmarshal v1.0.0:

error[E0277]: the trait bound `Error: StdError` is not satisfied
   --> /home/will/.cargo/registry/src/github.com-1ecc6299db9ec823/ssmarshal-1.0.0/src/lib.rs:64:6
    |
64  | impl serde::de::Error for Error {
    |      ^^^^^^^^^^^^^^^^ the trait `StdError` is not implemented for `Error`
    | 
   ::: /home/will/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.125/src/de/mod.rs:297:1
    |
297 | declare_error_trait!(Error: Sized + StdError);
    | ---------------------------------------------- required by this bound in `serde::de::Error`
... (one more of these)

Since your last release a few months ago, and the 1.0.0 version of ssmarshal was released 3 years ago, I figured you must not be seeing those?

I tested with latest stable rust v1.51.0 and additionally rustc version 1.49.0 (i.e. the version you may have been using at the time of last push to this repo?) but I'm still seeing the same errors. Unsure what else to try.

I realise this is not necessarily an issue with usbd-hid but the ssmarshal repo looks decidedly stale ๐Ÿ˜„ any ideas where I might be going wrong?

make device_class an enum

We can provide comments then so people know they want the HID one for mice, keyboards joystics etc vs googling

HIDClass wastefully allocates endpoints

In most cases USB HID Class implementations don't need both an EndpointOut and EndpointIn. This is especially painful for MCUs that have a limited number of endpoints (AVR, atsam4; 7 endpoints total).

Ideally the new (or some other designator) should indicate whether or not EndpointIn and/or EndpointOut is required for the given descriptor.

For example, Keyboards do not use an OUT endpoint as set_report is used instead to pass the LED information.

I'm going to try and ponder a PR that could address this issue.

    pub fn new<'a>(alloc: &'a UsbBusAllocator<B>, report_descriptor: &'static [u8], poll_ms: u8) -> HIDClass<'a, B> {
        HIDClass {
            if_num: alloc.interface(),
            out_ep: alloc.interrupt(64, poll_ms),
            in_ep: alloc.interrupt(64, poll_ms),
            report_descriptor: report_descriptor,
        }
    }

Cannot create Input report of larger than 32 bytes

Due to limitations of serde and serialize it seems that only arrays up to 32 are considered.

#[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = 0xFF1C, usage = 0x1100) = {
        rx=output;
        tx=input;
    }
)]
struct HidioReport {
    rx: [u8; 64],
    tx: [u8; 64],
}
error[E0277]: the trait bound `[u8; 64]: Serialize` is not satisfied
   --> usbd-hid-io/src/lib.rs:32:1
    |
32  | / #[gen_hid_descriptor(
33  | |     (collection = APPLICATION, usage_page = 0xFF1C, usage = 0x1100) = {
34  | |         rx=output;
35  | |         tx=input;
36  | |     }
37  | | )]
    | |  ^
    | |  |
    | |__the trait `Serialize` is not implemented for `[u8; 64]`
    |    in this macro invocation
    |
   ::: /home/hyatt/.cargo/registry/src/github.com-1ecc6299db9ec823/usbd-hid-macros-0.5.0/src/lib.rs:202:1
    |
202 |   pub fn gen_hid_descriptor(args: TokenStream, input: TokenStream) -> TokenStream {
    |   ------------------------------------------------------------------------------- in this expansion of `#[gen_hid_descriptor]`
    |
    = help: the following implementations were found:
              <[T; 0] as Serialize>
              <[T; 10] as Serialize>
              <[T; 11] as Serialize>
              <[T; 12] as Serialize>
            and 30 others

error: aborting due to previous error; 2 warnings emitted

For more information about this error, try `rustc --explain E0277`.
error: could not compile `usbd-hid-io`

I believe https://github.com/est31/serde-big-array can help, but I haven't figured out how to use it in this case.

Include redundant report descriptor fields

I use the gen_hid_descriptor macro like this:

#[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = 0xaaaa, usage = 0xaa) = {
        (usage = 0xbb,) = {
            input_buffer = input;
        };
        (usage = 0xcc,) = {
            output_buffer = output;
        };
    }
)]
pub struct Report {
    input_buffer: [u8; 64],
    output_buffer: [u8; 64],
}

the resulting descriptor looks like this:

0x06, 0xAA, 0xAA,  // Usage Page (Reserved 0xAAAA)
0x09, 0xAA,        // Usage (0xAA)
0xA1, 0x01,        // Collection (Application)
0x09, 0xAA,        //   Usage (0xBB)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x75, 0x08,        //   Report Size (8)
0x95, 0x40,        //   Report Count (64)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xBB,        //   Usage (0xCC)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

As you can see, the logical min & max, as well as report size & count are skipped for the output. I suspect this is correct and because they are identical to the input (I'm not an expert on HID). However, I'm need them to be in there for robust device discovery. It would be great if I could disable this optimization somehow.

Sticky volume keys when using MediaKeyboardReport

Im writing a USB volume control using a Pi Pico - and Im having issues with the volume control sticking or repeating on either direction (up or down) intermittently.

At first I thought that it was some sort of priority issue with the USB IRQ, but after going through the USB HID documentation I noticed that the media volume/up keys (0xe9 and 0xea usage ids) are marked as RTC or re-triggerable - which says they are re-triggered for as long as the key is "asserted."

Im not sure what asserted means in that regard - how does the USB host know when the key is released? I tried sending an additional Zero report right after the media key report - but that had no effect.

When debugging it I also noticed that whenever the key does "stick" (aka repeat), for as long as its repeated and sometimes for a few moments after my actual keyboard clicks on my actual keyboard don't register - which leads me back to believe that this has more to do with the USB device hogging the bus somehow.

A few more anecdotes -

  1. I looked at captures of the USB bus using Wireshark and USBcap - the messages do not repeat.
  2. the program doesn't work at all connected to my Mac - the USB IRQ doesnt fire and there's no USB communications, apart from a few messages at the beginning

Any ideas?

OS freezes thread on microcotroller

I'm not sure this issue is related to this crate, it can be deleted if it's considered off-topic.

I made a firmware for a macro keypad using this crate, initially everything worked fine, tested it on PCs with Fedora 36 Gnome, Ubuntu 22.04 and Raspberry Pi OS. The problem appeared on Fedora KDE, where after the device sends a few reports to the PC, the main thread seems to freeze, while the interrupt handler continues trigger. It only happens while logged in to the desktop environment, works fine in the login screen or on a non-graphical TTY.

Possibly relevant code from the project:

#[entry]
fn main() -> ! {
   /*
   ...
   Initialization code
   ...
   */
    loop {
        delay.delay_ms(30);

        let reports = keys::get_keys(keys);

        reports.into_iter().flatten().for_each(|report| {
            push_keyboard_report(report);
        });
    }
}

pub fn get_keys(keys: &[&dyn InputPin<Error = Infallible>]) -> [Option<KeyboardReport>; 9] {
    let mut ret = [None; 9];
    let mut pressed = false;

    for i in 0..keys.len() {
        if keys[i].is_low().unwrap() {
            let mut report = new_report();
            interrupt::free(|cs| {
                let buttons = &KEYS.borrow(cs).borrow().keys;
                report.modifier = buttons[i].modifier;
                report.keycodes = buttons[i].keycodes;
            });
            ret[i] = Some(report);
            pressed = true;
        }
    }

    if !pressed {
        ret[0] = Some(new_report());
    }

    ret
}

fn push_keyboard_report(report: KeyboardReport) -> Result<usize, usb_device::UsbError> {
    cortex_m::interrupt::free(|_| unsafe {
        USB_HID.as_mut().map(|hid| hid.push_input(&report))
    })
    .unwrap()
}

#[interrupt]
unsafe fn USBCTRL_IRQ() {
    // Handle USB request
    let usb_dev = USB_DEVICE.as_mut().unwrap();
    let usb_hid = USB_HID.as_mut().unwrap();
    //let serial = USB_SERIAL.as_mut().unwrap();

    usb_dev.poll(&mut [usb_hid]);
}

Tried to put defmt prints and it doesn't seem to freeze in the same place everytime. GDB doesn't really help because breakpoints also block the interrupt handling.

Any ideas on what could cause it or how could I continue to debug it?

Question about use-cases

Since this crate has little documentation (if I am wrong please tell me) and I am a newbie to usb communications, I thought I'd just ask.

I want to use a Raspberry Pi Zero 2W (2 USB Ports) as a middleman between an otherwise working usb connection.
In the first step I basically need to be wireshark, just as hardware.

The first port is the input, where the raspi is the HID host. The second port is the output, where the raspi has to simulate being the device (necessary hardware information is available) and be the HID client to send data to its unknown host.

Is this crate low-level enough for this task or is this more of an high-level abstraction? Since I have to be able to fake and complete control what my device sends, I'd think I need something quite low level

The generated Keyboard descriptor doesn't work under Windows

The library works fine on devices conencted to a linux machine but not a windows one.
After inspecting the usb initialisation sequence using wireshark, I noticed that wireshark also though the usbhid-report descriptor was malformed.

The offending Item is a '0x80'.
After comparing the generated descriptor with the example one in the HID spec I noticed it is an abbreviation for '0x81 0x00'.

Replacing the '0x80' with '0x81 0x00' and using that as the new descriptor made my device work under Windows.

I also found someone on stackoverflow who noticed similar behavior
Unspecified zero-sized items are not accepted by Windows HID parser (while they work on Mac OS, iOS and Linux). โ€“ Nipo May 7 '16 at 16:40 ( https://stackoverflow.com/questions/36750287/two-byte-report-count-for-hid-report-descriptor/36835781 )

usbd-hid/macros/src/lib.rs

Lines 335 to 344 in f0ddfa7

// Section 6.2.2.4: An Input item could have a data size of zero (0)
// bytes. In this case the value of each data bit for the item can be
// assumed to be zero. This is functionally identical to using a item
// tag that specifies a 4-byte data item followed by four zero bytes.
let allow_short = typ == ItemType::Main.into() && kind == MainItemKind::Input.into();
if allow_short && num == 0 {
prefix.set_byte_count(0);
elems.push(byte_literal(prefix.0));
return;
}

If I understand the code correctly, it shortens Input-Items with data '0x0' down to an input Item with size zero and no extra data.
This is apparently not supported under Windows even though the specification allows it.

Removing the mentioned lines would fix the problem.
I don't know a lot about USB hid though, so there might be a better way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.