Skip to content

Instantly share code, notes, and snippets.

@samcday
Created March 3, 2024 08:48
Show Gist options
  • Save samcday/44343e1f5cb22375639205aa0080564e to your computer and use it in GitHub Desktop.
Save samcday/44343e1f5cb22375639205aa0080564e to your computer and use it in GitHub Desktop.
usb-gadget HID test
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::sleep;
use std::time::Duration;
use usbd_hid_macros::gen_hid_descriptor;
use serde::ser::{Serialize, SerializeTuple, Serializer};
use usb_gadget::{Class, Config, default_udc, Gadget, Id, remove_all, Strings};
use usb_gadget::function::hid::Hid;
pub trait SerializedDescriptor {
fn desc() -> &'static [u8];
}
pub trait AsInputReport: Serialize {}
/// KeyboardReport describes a report and its companion descriptor that can be
/// used to send keyboard button presses to a host and receive the status of the
/// keyboard LEDs.
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
};
(usage_min = 0x00, usage_max = 0xFF) = {
#[item_settings constant,variable,absolute] reserved=input;
};
(usage_page = LEDS, usage_min = 0x01, usage_max = 0x05) = {
#[packed_bits 5] #[item_settings data,variable,absolute] leds=output;
};
(usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0xDD) = {
#[item_settings data,array,absolute] keycodes=input;
};
}
)]
#[allow(dead_code)]
pub struct KeyboardReport {
pub modifier: u8,
pub reserved: u8,
pub leds: u8,
pub keycodes: [u8; 6],
}
/// MouseReport describes a report and its companion descriptor than can be used
/// to send mouse movements and button presses to a host.
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = MOUSE) = {
(collection = PHYSICAL, usage = POINTER) = {
(usage_page = BUTTON, usage_min = BUTTON_1, usage_max = BUTTON_8) = {
#[packed_bits 8] #[item_settings data,variable,absolute] buttons=input;
};
(usage_page = GENERIC_DESKTOP,) = {
(usage = X,) = {
#[item_settings data,variable,relative] x=input;
};
(usage = Y,) = {
#[item_settings data,variable,relative] y=input;
};
(usage = WHEEL,) = {
#[item_settings data,variable,relative] wheel=input;
};
};
(usage_page = CONSUMER,) = {
(usage = AC_PAN,) = {
#[item_settings data,variable,relative] pan=input;
};
};
};
}
)]
#[allow(dead_code)]
pub struct MouseReport {
pub buttons: u8,
pub x: i8,
pub y: i8,
pub wheel: i8, // Scroll down (negative) or up (positive) this many units
pub pan: i8, // Scroll left (negative) or right (positive) this many units
}
fn main() {
let udc = default_udc().expect("no UDC found");
remove_all().expect("failed to clear gadgets");
let mut builder = Hid::builder();
builder.protocol = 1;
builder.report_len = 8;
builder.report_desc = KeyboardReport::desc().to_vec();
let (kbhid, kbhandle) = builder.build();
let mut builder = Hid::builder();
builder.protocol = 2;
builder.report_len = 5;
builder.report_desc = MouseReport::desc().to_vec();
let (mousehid, mousehandle) = builder.build();
let _reg = Gadget::new(Class::new(0, 0, 0), Id::new(666, 666), Strings::new("Test", "FakeKeeb", "666"))
.with_config(Config::new("kbmouse").with_function(kbhandle).with_function(mousehandle))
.bind(&udc)
.expect("gadget binding failed");
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
}).expect("cleanup handler registration failed");
println!("running");
sleep(Duration::from_millis(1000));
let (kb_major, kb_minor) = kbhid.device().unwrap();
let mut kb = File::options().append(true).open(PathBuf::from(format!("/dev/char/{}:{}", kb_major, kb_minor))).expect("failed to open kb dev");
let (mouse_major, mouse_minor) = mousehid.device().unwrap();
let mut mouse = File::options().append(true).open(PathBuf::from(format!("/dev/char/{}:{}", mouse_major, mouse_minor))).expect("failed to open mouse dev");
let mut kbbuf: [u8; 8] = [0; 8];
let mut kbreport = KeyboardReport{
modifier: 0,
reserved: 0,
leds: 0,
keycodes: [4, 0, 0, 0, 0, 0],
};
ssmarshal::serialize(&mut kbbuf, &kbreport).expect("report serialization");
kb.write_all(&kbbuf).expect("keyboard report write failed");
kbreport.keycodes[0] = 0;
ssmarshal::serialize(&mut kbbuf, &kbreport).expect("report serialization");
kb.write_all(&kbbuf).expect("keyboard report write failed");
let mut mousebuf: [u8; 5] = [0; 5];
let mousereport = MouseReport{
x: 10,
y: 10,
buttons: 0,
pan: 0,
wheel: 0,
};
ssmarshal::serialize(&mut mousebuf, &mousereport).expect("report serialization");
mouse.write_all(&mousebuf).expect("mouse report write failed");
while running.load(Ordering::SeqCst) {
sleep(Duration::from_millis(100));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment