use core::{future::Future, sync::atomic::Ordering};
use embassy_usb::class::hid::ReadError;
use embassy_usb::driver::EndpointError;
use serde::Serialize;
use usbd_hid::descriptor::{AsInputReport, MediaKeyboardReport, MouseReport, SystemControlReport};
use crate::CONNECTION_STATE;
use crate::channel::KEYBOARD_REPORT_CHANNEL;
use crate::descriptor::KeyboardReport;
use crate::state::ConnectionState;
#[cfg(not(feature = "_no_usb"))]
use crate::usb::USB_REMOTE_WAKEUP;
#[derive(Serialize, Debug, Clone)]
pub enum Report {
KeyboardReport(KeyboardReport),
MouseReport(MouseReport),
MediaKeyboardReport(MediaKeyboardReport),
SystemControlReport(SystemControlReport),
}
impl AsInputReport for Report {}
#[derive(PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HidError {
UsbReadError(ReadError),
UsbEndpointError(EndpointError),
UsbDisabled,
UsbPartialRead,
BufferOverflow,
ReportSerializeError,
BleError,
}
pub trait HidWriterTrait {
type ReportType: AsInputReport + Clone;
fn write_report(&mut self, report: Self::ReportType) -> impl Future<Output = Result<usize, HidError>>;
}
pub trait RunnableHidWriter: HidWriterTrait {
fn get_report(&mut self) -> impl Future<Output = Self::ReportType>;
fn run_writer(&mut self) -> impl Future<Output = ()> {
async {
loop {
let report = self.get_report().await;
if CONNECTION_STATE.load(Ordering::Acquire)
== <ConnectionState as Into<bool>>::into(ConnectionState::Connected)
&& let Err(e) = self.write_report(report.clone()).await
{
error!("Failed to send report: {:?}", e);
#[cfg(not(feature = "_no_usb"))]
if let HidError::UsbEndpointError(EndpointError::Disabled) = e {
USB_REMOTE_WAKEUP.signal(());
embassy_time::Timer::after_millis(200).await;
if let Err(e) = self.write_report(report).await {
error!("Failed to send report after wakeup: {:?}", e);
}
}
};
}
}
}
}
pub trait HidReaderTrait {
type ReportType;
fn read_report(&mut self) -> impl Future<Output = Result<Self::ReportType, HidError>>;
}
pub struct DummyWriter {}
impl HidWriterTrait for DummyWriter {
type ReportType = Report;
async fn write_report(&mut self, _report: Self::ReportType) -> Result<usize, HidError> {
Ok(0)
}
}
impl RunnableHidWriter for DummyWriter {
async fn run_writer(&mut self) {
CONNECTION_STATE.store(ConnectionState::Connected.into(), Ordering::Release);
loop {
let _ = KEYBOARD_REPORT_CHANNEL.receive().await;
}
}
async fn get_report(&mut self) -> Self::ReportType {
panic!("`get_report` in Dummy writer should not be used");
}
}
#[cfg(feature = "_nrf_ble")]
pub(crate) fn get_serial_number() -> &'static str {
use heapless::String;
use static_cell::StaticCell;
static SERIAL: StaticCell<String<20>> = StaticCell::new();
let serial = SERIAL.init_with(|| {
let ficr = embassy_nrf::pac::FICR;
let device_id = (u64::from(ficr.deviceid(1).read()) << 32) | u64::from(ficr.deviceid(0).read());
let mut result = String::new();
let _ = result.push_str("vial:f64c2b3c:");
const HEX_TABLE: &[u8] = b"0123456789abcdef";
for i in 0..6 {
let digit = (device_id >> (60 - i * 4)) & 0xF;
let hex_char = HEX_TABLE[digit as usize] as char;
let _ = result.push(hex_char);
}
result
});
serial.as_str()
}