Get a dummy keyboard working
This commit is contained in:
185
src/usb/keyboard.rs
Normal file
185
src/usb/keyboard.rs
Normal file
@ -0,0 +1,185 @@
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::{
|
||||
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
||||
control::OutResponse,
|
||||
Builder,
|
||||
};
|
||||
use embassy_usb_driver::EndpointError;
|
||||
use static_cell::StaticCell;
|
||||
use usbd_hid::descriptor::{KeyboardReport, MouseReport, SerializedDescriptor};
|
||||
|
||||
use crate::keyboard::{Button, COLS, MATRIX, ROWS, TEST_KEYMAP};
|
||||
|
||||
use super::MAX_PACKET_SIZE;
|
||||
|
||||
struct Handler;
|
||||
|
||||
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
||||
|
||||
struct Context {
|
||||
handler: Handler,
|
||||
state: hid::State<'static>,
|
||||
}
|
||||
|
||||
pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>) {
|
||||
log::info!("setting up usb hid");
|
||||
|
||||
let context = CONTEXT.init(Context {
|
||||
handler: Handler,
|
||||
state: hid::State::new(),
|
||||
});
|
||||
|
||||
let config = hid::Config {
|
||||
//report_descriptor: MouseReport::desc(),
|
||||
report_descriptor: KeyboardReport::desc(),
|
||||
request_handler: Some(&context.handler),
|
||||
poll_ms: 2,
|
||||
max_packet_size: MAX_PACKET_SIZE as u16,
|
||||
};
|
||||
|
||||
let stream = HidStream::new(builder, &mut context.state, config);
|
||||
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
spawner.must_spawn(task(stream, &context.handler));
|
||||
|
||||
log::info!("done setting up usb keyboard");
|
||||
}
|
||||
|
||||
impl RequestHandler for Handler {
|
||||
fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
|
||||
log::info!("get_report({id:?}, {buf:?})");
|
||||
let _ = (id, buf);
|
||||
None
|
||||
}
|
||||
|
||||
fn set_report(&self, id: ReportId, data: &[u8]) -> embassy_usb::control::OutResponse {
|
||||
log::info!("set_report({id:?}, {data:?})");
|
||||
let _ = (id, data);
|
||||
OutResponse::Rejected
|
||||
}
|
||||
|
||||
fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
|
||||
log::info!("get_idle_ms({id:?})");
|
||||
let _ = id;
|
||||
None
|
||||
}
|
||||
|
||||
fn set_idle_ms(&self, id: Option<ReportId>, duration_ms: u32) {
|
||||
log::info!("set_idle_ms({id:?}, {duration_ms})");
|
||||
let _ = (id, duration_ms);
|
||||
}
|
||||
}
|
||||
type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task(stream: HidStream, handler: &'static Handler) {
|
||||
if let Err(e) = keyboard_test(stream, handler).await {
|
||||
log::error!("keyboard error: {e:?}");
|
||||
}
|
||||
//if let Err(e) = mouse_wiggler(stream).await {
|
||||
// log::error!("mouse wiggler: {e:?}");
|
||||
//}
|
||||
}
|
||||
|
||||
async fn keyboard_test(mut stream: HidStream, handler: &'static Handler) -> Result<(), Error> {
|
||||
stream.ready().await;
|
||||
loop {
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
|
||||
let keymap = &TEST_KEYMAP;
|
||||
|
||||
let mut keycodes = [0u8; 6];
|
||||
let mut i = 0;
|
||||
|
||||
'keyscan: for col in 0..COLS {
|
||||
for row in 0..ROWS {
|
||||
if !MATRIX[row][col].is_pressed() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Button::Key { keycode } = &keymap[row][col];
|
||||
// else { continue; };
|
||||
|
||||
keycodes[i] = *keycode;
|
||||
i += 1;
|
||||
if i >= keycodes.len() {
|
||||
break 'keyscan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if keycodes.iter().any(|&b| b != 0) {
|
||||
log::info!("keycodes: {keycodes:?}");
|
||||
}
|
||||
stream
|
||||
.write_serialize(&KeyboardReport {
|
||||
modifier: 0,
|
||||
reserved: 0,
|
||||
leds: 0,
|
||||
keycodes,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn mouse_wiggler(mut stream: HidStream) -> Result<(), Error> {
|
||||
stream.ready().await;
|
||||
|
||||
let (_r, mut w) = stream.split();
|
||||
|
||||
let write_fut = async move {
|
||||
let mut x = 1;
|
||||
loop {
|
||||
for _ in 0..100 {
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
log::info!("sending mouse report");
|
||||
//w.ready().await;
|
||||
w.write_serialize(&MouseReport {
|
||||
x,
|
||||
y: 0,
|
||||
buttons: 0,
|
||||
wheel: 0,
|
||||
pan: 0,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
x = -x;
|
||||
}
|
||||
};
|
||||
|
||||
//let read_fut = async move {
|
||||
// let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||
// loop {
|
||||
// Timer::after(Duration::from_millis(30)).await;
|
||||
// let n = r.read(&mut buf).await?;
|
||||
// log::info!("got packet: {:?}", &buf[..n]);
|
||||
// }
|
||||
//};
|
||||
|
||||
//let r: Result<((), ()), Error> = try_join(write_fut, read_fut).await;
|
||||
let r: Result<(), Error> = write_fut.await;
|
||||
r?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Read(ReadError),
|
||||
Endpoint(EndpointError),
|
||||
}
|
||||
|
||||
impl From<ReadError> for Error {
|
||||
fn from(value: ReadError) -> Self {
|
||||
Error::Read(value)
|
||||
}
|
||||
}
|
||||
impl From<EndpointError> for Error {
|
||||
fn from(value: EndpointError) -> Self {
|
||||
Error::Endpoint(value)
|
||||
}
|
||||
}
|
||||
79
src/usb/logger.rs
Normal file
79
src/usb/logger.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use super::MAX_PACKET_SIZE;
|
||||
use core::fmt::Write as WriteFmt;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||
use embassy_time::Instant;
|
||||
use embassy_usb::{
|
||||
class::cdc_acm::{self, CdcAcmClass},
|
||||
Builder,
|
||||
};
|
||||
use log::{Metadata, Record};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
pub const BUFFER_SIZE: usize = 16 * 1024;
|
||||
static BUFFER: Pipe<CriticalSectionRawMutex, BUFFER_SIZE> = Pipe::new();
|
||||
static STATE: StaticCell<cdc_acm::State<'static>> = StaticCell::new();
|
||||
|
||||
struct UsbLogger;
|
||||
|
||||
pub async fn setup(usb_builder: &mut Builder<'static, Driver<'static, USB>>) {
|
||||
unsafe {
|
||||
static LOGGER: UsbLogger = UsbLogger;
|
||||
log::set_logger_racy(&LOGGER).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
}
|
||||
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
let state = STATE.init(cdc_acm::State::new());
|
||||
|
||||
let class = CdcAcmClass::new(usb_builder, state, MAX_PACKET_SIZE as u16);
|
||||
|
||||
spawner.must_spawn(log_task(class));
|
||||
}
|
||||
|
||||
//pub async fn print(s: &str) {
|
||||
// BUFFER.writer().write_all(s.as_bytes()).await.ok(/* infallible */);
|
||||
//}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn log_task(mut class: CdcAcmClass<'static, Driver<'static, USB>>) {
|
||||
let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||
|
||||
class.wait_connection().await;
|
||||
loop {
|
||||
let n = BUFFER.read(&mut buf).await;
|
||||
|
||||
// not much we can do if this fails, just ignore the error
|
||||
let _ = class.write_packet(&buf[..n]).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for UsbLogger {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
let mut w = Writer;
|
||||
let now = Instant::now();
|
||||
let s = now.as_secs();
|
||||
let ms = now.as_millis() % 1000;
|
||||
let level = record.metadata().level();
|
||||
let _ = write!(w, "[{s}.{ms:04}] ({level}) {}\n", record.args());
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
struct Writer;
|
||||
|
||||
impl core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
let _ = BUFFER.try_write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user