Created
March 3, 2024 08:48
-
-
Save samcday/44343e1f5cb22375639205aa0080564e to your computer and use it in GitHub Desktop.
usb-gadget HID test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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