use crate::page::Keyboard;
use crate::usb_class::prelude::*;
use fugit::ExtU32;
use packed_struct::prelude::*;
#[allow(clippy::wildcard_imports)]
use usb_device::class_prelude::*;
use usb_device::UsbError;
pub struct BootKeyboard<'a, B: UsbBus> {
interface: ManagedIdleInterface<'a, B, BootKeyboardReport, InBytes8, OutBytes8>,
}
impl<B> BootKeyboard<'_, B>
where
B: UsbBus,
{
pub fn write_report<K: IntoIterator<Item = Keyboard>>(
&mut self,
keys: K,
) -> Result<(), UsbHidError> {
self.interface.write_report(&BootKeyboardReport::new(keys))
}
pub fn read_report(&mut self) -> usb_device::Result<KeyboardLedsReport> {
let data = &mut [0];
match self.interface.read_report(data) {
Err(e) => Err(e),
Ok(_) => match KeyboardLedsReport::unpack(data) {
Ok(r) => Ok(r),
Err(_) => Err(UsbError::ParseError),
},
}
}
}
impl<'a, B> DeviceClass<'a> for BootKeyboard<'a, B>
where
B: UsbBus,
{
type I = Interface<'a, B, InBytes8, OutBytes8, ReportSingle>;
fn interface(&mut self) -> &mut Self::I {
self.interface.interface()
}
fn reset(&mut self) {
self.interface.reset();
}
fn tick(&mut self) -> Result<(), UsbHidError> {
self.interface.tick()
}
}
pub struct BootKeyboardConfig<'a> {
interface: ManagedIdleInterfaceConfig<'a, BootKeyboardReport, InBytes8, OutBytes8>,
}
impl Default for BootKeyboardConfig<'_> {
fn default() -> Self {
Self::new(ManagedIdleInterfaceConfig::new(
unwrap!(unwrap!(unwrap!(unwrap!(InterfaceBuilder::new(
BOOT_KEYBOARD_REPORT_DESCRIPTOR
))
.boot_device(InterfaceProtocol::Keyboard)
.description("Keyboard")
.idle_default(500.millis()))
.in_endpoint(10.millis()))
.with_out_endpoint(100.millis()))
.build(),
))
}
}
impl<'a> BootKeyboardConfig<'a> {
#[must_use]
pub fn new(
interface: ManagedIdleInterfaceConfig<'a, BootKeyboardReport, InBytes8, OutBytes8>,
) -> Self {
Self { interface }
}
}
impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for BootKeyboardConfig<'a> {
type Allocated = BootKeyboard<'a, B>;
fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
Self::Allocated {
interface: self.interface.allocate(usb_alloc),
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
#[packed_struct(endian = "lsb", bit_numbering = "lsb0", size_bytes = "1")]
pub struct KeyboardLedsReport {
#[packed_field(bits = "0")]
pub num_lock: bool,
#[packed_field(bits = "1")]
pub caps_lock: bool,
#[packed_field(bits = "2")]
pub scroll_lock: bool,
#[packed_field(bits = "3")]
pub compose: bool,
#[packed_field(bits = "4")]
pub kana: bool,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
#[packed_struct(endian = "lsb", bit_numbering = "msb0", size_bytes = "8")]
pub struct BootKeyboardReport {
#[packed_field(bits = "0")]
pub right_gui: bool,
#[packed_field(bits = "1")]
pub right_alt: bool,
#[packed_field(bits = "2")]
pub right_shift: bool,
#[packed_field(bits = "3")]
pub right_ctrl: bool,
#[packed_field(bits = "4")]
pub left_gui: bool,
#[packed_field(bits = "5")]
pub left_alt: bool,
#[packed_field(bits = "6")]
pub left_shift: bool,
#[packed_field(bits = "7")]
pub left_ctrl: bool,
#[packed_field(bytes = "2..8", ty = "enum", element_size_bytes = "1")]
pub keys: [Keyboard; 6],
}
impl BootKeyboardReport {
pub fn new<K: IntoIterator<Item = Keyboard>>(keys: K) -> Self {
let mut report = Self::default();
let mut error = false;
let mut i = 0;
for k in keys {
match k {
Keyboard::LeftControl => {
report.left_ctrl = true;
}
Keyboard::LeftShift => {
report.left_shift = true;
}
Keyboard::LeftAlt => {
report.left_alt = true;
}
Keyboard::LeftGUI => {
report.left_gui = true;
}
Keyboard::RightControl => {
report.right_ctrl = true;
}
Keyboard::RightShift => {
report.right_shift = true;
}
Keyboard::RightAlt => {
report.right_alt = true;
}
Keyboard::RightGUI => {
report.right_gui = true;
}
Keyboard::NoEventIndicated => {}
Keyboard::ErrorRollOver | Keyboard::POSTFail | Keyboard::ErrorUndefine => {
if !error {
error = true;
i = report.keys.len();
report.keys.fill(k);
}
}
_ => {
if error {
continue;
}
if i < report.keys.len() {
report.keys[i] = k;
i += 1;
} else {
error = true;
i = report.keys.len();
report.keys.fill(Keyboard::ErrorRollOver);
}
}
}
}
report
}
}
#[rustfmt::skip]
pub const BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2A, 0xFF, 0x00, 0x81, 0x00, 0xC0, ];
#[rustfmt::skip]
pub const NKRO_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x75, 0x38, 0x95, 0x01, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x88, 0x75, 0x01, 0x15, 0x00, 0x25, 0x01, 0x05, 0x07, 0x19, 0x00, 0x29, 0x87, 0x81, 0x02, 0xc0 ];
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
#[packed_struct(endian = "lsb", bit_numbering = "msb0", size_bytes = "25")]
pub struct NKROBootKeyboardReport {
#[packed_field(bits = "0")]
pub right_gui: bool,
#[packed_field(bits = "1")]
pub right_alt: bool,
#[packed_field(bits = "2")]
pub right_shift: bool,
#[packed_field(bits = "3")]
pub right_ctrl: bool,
#[packed_field(bits = "4")]
pub left_gui: bool,
#[packed_field(bits = "5")]
pub left_alt: bool,
#[packed_field(bits = "6")]
pub left_shift: bool,
#[packed_field(bits = "7")]
pub left_ctrl: bool,
#[packed_field(bytes = "2..8", ty = "enum", element_size_bytes = "1")]
pub boot_keys: [Keyboard; 6],
#[packed_field(bytes = "8..25", element_size_bits = "8")]
pub nkro_keys: [u8; 17],
}
impl NKROBootKeyboardReport {
pub fn new<K: IntoIterator<Item = Keyboard>>(keys: K) -> Self {
let mut report = Self::default();
let mut boot_keys_error = false;
let mut i = 0;
for k in keys {
match k {
Keyboard::LeftControl => {
report.left_ctrl = true;
}
Keyboard::LeftShift => {
report.left_shift = true;
}
Keyboard::LeftAlt => {
report.left_alt = true;
}
Keyboard::LeftGUI => {
report.left_gui = true;
}
Keyboard::RightControl => {
report.right_ctrl = true;
}
Keyboard::RightShift => {
report.right_shift = true;
}
Keyboard::RightAlt => {
report.right_alt = true;
}
Keyboard::RightGUI => {
report.right_gui = true;
}
Keyboard::NoEventIndicated => {}
Keyboard::ErrorRollOver | Keyboard::POSTFail | Keyboard::ErrorUndefine => {
report.nkro_keys[0] |= 1 << u8::from(k);
if !boot_keys_error {
boot_keys_error = true;
i = report.boot_keys.len();
report.boot_keys.fill(k);
}
}
_ => {
if report.nkro_keys.len() * 8 > u8::from(k).into() {
let byte = u8::from(k) / 8;
let bit = u8::from(k) % 8;
report.nkro_keys[usize::from(byte)] |= 1 << bit;
}
if boot_keys_error {
continue;
}
if i < report.boot_keys.len() {
report.boot_keys[i] = k;
i += 1;
} else {
boot_keys_error = true;
i = report.boot_keys.len();
report.boot_keys.fill(Keyboard::ErrorRollOver);
}
}
}
}
report
}
}
pub struct NKROBootKeyboard<'a, B: UsbBus> {
interface: ManagedIdleInterface<'a, B, NKROBootKeyboardReport, InBytes32, OutBytes8>,
}
impl<B> NKROBootKeyboard<'_, B>
where
B: UsbBus,
{
pub fn write_report<K: IntoIterator<Item = Keyboard>>(
&mut self,
keys: K,
) -> Result<(), UsbHidError> {
self.interface
.write_report(&NKROBootKeyboardReport::new(keys))
}
pub fn read_report(&mut self) -> usb_device::Result<KeyboardLedsReport> {
let data = &mut [0];
match self.interface.read_report(data) {
Err(e) => Err(e),
Ok(_) => match KeyboardLedsReport::unpack(data) {
Ok(r) => Ok(r),
Err(_) => Err(UsbError::ParseError),
},
}
}
}
pub struct NKROBootKeyboardConfig<'a> {
interface: ManagedIdleInterfaceConfig<'a, NKROBootKeyboardReport, InBytes32, OutBytes8>,
}
impl Default for NKROBootKeyboardConfig<'_> {
fn default() -> Self {
Self::new(ManagedIdleInterfaceConfig::new(
unwrap!(unwrap!(unwrap!(unwrap!(InterfaceBuilder::new(
NKRO_BOOT_KEYBOARD_REPORT_DESCRIPTOR
))
.description("NKRO Keyboard")
.boot_device(InterfaceProtocol::Keyboard)
.idle_default(500.millis()))
.in_endpoint(10.millis()))
.with_out_endpoint(100.millis()))
.build(),
))
}
}
impl<'a> NKROBootKeyboardConfig<'a> {
#[must_use]
pub fn new(
interface: ManagedIdleInterfaceConfig<'a, NKROBootKeyboardReport, InBytes32, OutBytes8>,
) -> Self {
Self { interface }
}
}
impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for NKROBootKeyboardConfig<'a> {
type Allocated = NKROBootKeyboard<'a, B>;
fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
Self::Allocated {
interface: self.interface.allocate(usb_alloc),
}
}
}
impl<'a, B> DeviceClass<'a> for NKROBootKeyboard<'a, B>
where
B: UsbBus,
{
type I = Interface<'a, B, InBytes32, OutBytes8, ReportSingle>;
fn interface(&mut self) -> &mut Self::I {
self.interface.interface()
}
fn reset(&mut self) {
self.interface.reset();
}
fn tick(&mut self) -> core::result::Result<(), UsbHidError> {
self.interface.tick()
}
}
#[rustfmt::skip]
pub const NKRO_COMPACT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x88, 0x75, 0x01, 0x15, 0x00, 0x25, 0x01, 0x05, 0x07, 0x19, 0x00, 0x29, 0x87, 0x81, 0x02, 0xc0 ];
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use packed_struct::prelude::*;
use crate::device::keyboard::{BootKeyboardReport, KeyboardLedsReport};
use crate::page::Keyboard;
#[test]
fn leds_num_lock() {
assert_eq!(
KeyboardLedsReport::unpack(&[1]),
Ok(KeyboardLedsReport {
num_lock: true,
caps_lock: false,
scroll_lock: false,
compose: false,
kana: false,
})
);
}
#[test]
fn leds_caps_lock() {
assert_eq!(
KeyboardLedsReport::unpack(&[2]),
Ok(KeyboardLedsReport {
num_lock: false,
caps_lock: true,
scroll_lock: false,
compose: false,
kana: false,
})
);
assert_eq!(
KeyboardLedsReport {
num_lock: false,
caps_lock: true,
scroll_lock: false,
compose: false,
kana: false,
}
.pack(),
Ok([2])
);
}
#[test]
fn boot_keyboard_report_mixed() {
let bytes = BootKeyboardReport::new([
Keyboard::LeftAlt,
Keyboard::A,
Keyboard::B,
Keyboard::C,
Keyboard::RightGUI,
])
.pack()
.unwrap();
let key_mod: u8 = (0x1_u8
<< (u8::from(Keyboard::LeftAlt) - u8::from(Keyboard::LeftControl)))
| (0x1_u8 << (u8::from(Keyboard::RightGUI) - u8::from(Keyboard::LeftControl)));
assert_eq!(
bytes,
[
key_mod,
0,
Keyboard::A.into(),
Keyboard::B.into(),
Keyboard::C.into(),
0,
0,
0
]
);
}
#[test]
fn boot_keyboard_report_keys() {
let bytes = BootKeyboardReport::new([
Keyboard::A,
Keyboard::B,
Keyboard::C,
Keyboard::D,
Keyboard::E,
Keyboard::F,
])
.pack()
.unwrap();
assert_eq!(
bytes,
[
0,
0,
Keyboard::A.into(),
Keyboard::B.into(),
Keyboard::C.into(),
Keyboard::D.into(),
Keyboard::E.into(),
Keyboard::F.into()
]
);
}
#[test]
fn boot_keyboard_report_rollover() {
let bytes = BootKeyboardReport::new([
Keyboard::LeftAlt,
Keyboard::A,
Keyboard::B,
Keyboard::C,
Keyboard::D,
Keyboard::E,
Keyboard::F,
Keyboard::G,
Keyboard::RightGUI,
])
.pack()
.unwrap();
let key_mod: u8 = (0x1_u8
<< (u8::from(Keyboard::LeftAlt) - u8::from(Keyboard::LeftControl)))
| (0x1_u8 << (u8::from(Keyboard::RightGUI) - u8::from(Keyboard::LeftControl)));
assert_eq!(
bytes,
[
key_mod,
0,
Keyboard::ErrorRollOver.into(),
Keyboard::ErrorRollOver.into(),
Keyboard::ErrorRollOver.into(),
Keyboard::ErrorRollOver.into(),
Keyboard::ErrorRollOver.into(),
Keyboard::ErrorRollOver.into(),
]
);
}
}