Skip to content

Commit

Permalink
replace hid send/receive channels with hiddevice trait
Browse files Browse the repository at this point in the history
  • Loading branch information
Univa committed Apr 3, 2024
1 parent 030e9a0 commit 297ae98
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 89 deletions.
14 changes: 9 additions & 5 deletions rumcake-macros/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ pub(crate) fn keyboard_main(
};

if keyboard.bluetooth || keyboard.usb {
outer.extend(quote! {
impl ::rumcake::hw::HIDDevice for #kb_name {}
});
spawning.extend(quote! {
spawner.spawn(::rumcake::layout_collect!(#kb_name)).unwrap();
});
Expand Down Expand Up @@ -538,7 +541,7 @@ pub(crate) fn keyboard_main(
spawner.spawn(::rumcake::start_usb!(usb)).unwrap();

// HID Keyboard Report sending
spawner.spawn(::rumcake::usb_hid_kb_write_task!(kb_class)).unwrap();
spawner.spawn(::rumcake::usb_hid_kb_write_task!(#kb_name, kb_class)).unwrap();
});

if cfg!(feature = "media-keycodes") {
Expand All @@ -548,25 +551,26 @@ pub(crate) fn keyboard_main(
});
spawning.extend(quote! {
// HID Consumer Report sending
spawner.spawn(::rumcake::usb_hid_consumer_write_task!(consumer_class)).unwrap();
spawner.spawn(::rumcake::usb_hid_consumer_write_task!(#kb_name, consumer_class)).unwrap();
});
}
}

if keyboard.usb && (keyboard.via.is_some() || keyboard.vial.is_some()) {
initialization.extend(quote! {
static VIA_COMMAND_HANDLER: ::rumcake::usb::ViaCommandHandler<#kb_name> = ::rumcake::usb::ViaCommandHandler::new();
// Via HID setup
let (via_reader, via_writer) =
::rumcake::usb::setup_usb_via_hid_reader_writer(&mut builder).split();
::rumcake::usb::setup_usb_via_hid_reader_writer(&VIA_COMMAND_HANDLER, &mut builder).split();
});
spawning.extend(quote! {
// HID raw report (for VIA) reading and writing
spawner
.spawn(::rumcake::usb_hid_via_read_task!(via_reader))
.spawn(::rumcake::usb_hid_via_read_task!(&VIA_COMMAND_HANDLER, via_reader))
.unwrap();
});
spawning.extend(quote! {
spawner.spawn(::rumcake::usb_hid_via_write_task!(via_writer)).unwrap();
spawner.spawn(::rumcake::usb_hid_via_write_task!(#kb_name, via_writer)).unwrap();
});
}

Expand Down
3 changes: 2 additions & 1 deletion rumcake/src/bluetooth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ pub mod nrf_ble;
use embassy_sync::signal::Signal;

use crate::hw::platform::RawMutex;
use crate::hw::HIDDevice;
use crate::keyboard::Keyboard;
use crate::State;

/// A trait that keyboards must implement to communicate with host devices over Bluetooth (LE).
pub trait BluetoothKeyboard: Keyboard {
pub trait BluetoothKeyboard: Keyboard + HIDDevice {
/// Vendor ID for the keyboard.
const BLE_VID: u16;

Expand Down
49 changes: 27 additions & 22 deletions rumcake/src/bluetooth/nrf_ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use usbd_human_interface_device::device::keyboard::NKROBootKeyboardReport;

use crate::hw::platform::BLUETOOTH_ADVERTISING_MUTEX;
use crate::hw::{HIDOutput, BATTERY_LEVEL_STATE, CURRENT_OUTPUT_STATE};
use crate::keyboard::{CONSUMER_REPORT_HID_SEND_CHANNEL, KEYBOARD_REPORT_HID_SEND_CHANNEL};

use crate::bluetooth::{
BluetoothKeyboard, BATTERY_LEVEL_LISTENER, BLUETOOTH_CONNECTED_STATE,
Expand Down Expand Up @@ -776,16 +775,19 @@ where
}
HIDServiceEvent::ViaReportWrite(report) => {
#[cfg(feature = "via")]
match crate::via::VIA_REPORT_HID_RECEIVE_CHANNEL.try_send(report) {
Ok(()) => {
debug!("[BT_HID] Received Via report: {}", report);
}
Err(err) => {
error!(
"[BT_HID] Could not consume Via report. data: {:?} error: {:?}",
Debug2Format(&report),
Debug2Format(&err)
);
{
let channel = K::get_via_hid_receive_channel();
match channel.try_send(report) {
Ok(()) => {
debug!("[BT_HID] Received Via report: {}", report);
}
Err(err) => {
error!(
"[BT_HID] Could not consume Via report. data: {:?} error: {:?}",
Debug2Format(&report),
Debug2Format(&err)
);
}
}
}

Expand Down Expand Up @@ -822,24 +824,27 @@ where
};

let hid_fut = async {
let keyboard_report_channel = K::get_keyboard_report_send_channel();
let consumer_report_channel = K::get_consumer_report_send_channel();

// Discard any reports that haven't been processed due to lack of a connection
while KEYBOARD_REPORT_HID_SEND_CHANNEL.try_receive().is_ok() {}
while CONSUMER_REPORT_HID_SEND_CHANNEL.try_receive().is_ok() {}
while keyboard_report_channel.try_receive().is_ok() {}
while consumer_report_channel.try_receive().is_ok() {}

#[cfg(feature = "via")]
let via_report_channel = K::get_via_hid_send_channel();

#[cfg(feature = "via")]
while crate::via::VIA_REPORT_HID_SEND_CHANNEL
.try_receive()
.is_ok()
{}
while via_report_channel.try_receive().is_ok() {}

loop {
if matches!(CURRENT_OUTPUT_STATE.get().await, Some(HIDOutput::Bluetooth)) {
#[cfg(feature = "via")]
match select4(
CURRENT_OUTPUT_STATE_LISTENER.wait(),
KEYBOARD_REPORT_HID_SEND_CHANNEL.receive(),
CONSUMER_REPORT_HID_SEND_CHANNEL.receive(),
crate::via::VIA_REPORT_HID_SEND_CHANNEL.receive(),
keyboard_report_channel.receive(),
consumer_report_channel.receive(),
via_report_channel.receive(),
)
.await
{
Expand Down Expand Up @@ -892,8 +897,8 @@ where
#[cfg(not(feature = "via"))]
match select3(
CURRENT_OUTPUT_STATE_LISTENER.wait(),
KEYBOARD_REPORT_HID_SEND_CHANNEL.receive(),
CONSUMER_REPORT_HID_SEND_CHANNEL.receive(),
keyboard_report_channel.receive(),
consumer_report_channel.receive(),
)
.await
{
Expand Down
28 changes: 28 additions & 0 deletions rumcake/src/hw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use embassy_time::Timer;
use embedded_hal::digital::v2::OutputPin;

use platform::RawMutex;
use usbd_human_interface_device::device::consumer::MultipleConsumerReport;
use usbd_human_interface_device::device::keyboard::NKROBootKeyboardReport;

/// State that contains the current battery level. `rumcake` may or may not use this static
/// internally, depending on what MCU is being used. The contents of this state is usually set by a
Expand Down Expand Up @@ -258,3 +260,29 @@ impl<E, T: OutputPin<Error = E>, const P: usize> Multiplexer<T, P> {
Ok(())
}
}

pub trait HIDDevice {
fn get_keyboard_report_send_channel() -> &'static Channel<RawMutex, NKROBootKeyboardReport, 1> {
static KEYBOARD_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, NKROBootKeyboardReport, 1> =
Channel::new();
&KEYBOARD_REPORT_HID_SEND_CHANNEL
}

fn get_consumer_report_send_channel() -> &'static Channel<RawMutex, MultipleConsumerReport, 1> {
static CONSUMER_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, MultipleConsumerReport, 1> =
Channel::new();
&CONSUMER_REPORT_HID_SEND_CHANNEL
}

#[cfg(feature = "via")]
fn get_via_hid_send_channel() -> &'static Channel<RawMutex, [u8; 32], 1> {
static VIA_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, [u8; 32], 1> = Channel::new();
&VIA_REPORT_HID_SEND_CHANNEL
}

#[cfg(feature = "via")]
fn get_via_hid_receive_channel() -> &'static Channel<RawMutex, [u8; 32], 1> {
static VIA_REPORT_HID_RECEIVE_CHANNEL: Channel<RawMutex, [u8; 32], 1> = Channel::new();
&VIA_REPORT_HID_RECEIVE_CHANNEL
}
}
37 changes: 13 additions & 24 deletions rumcake/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use core::ops::Range;

use defmt::{debug, info, warn, Debug2Format};
use embassy_sync::channel::Channel;
use embassy_sync::mutex::{Mutex, MutexGuard};
use embassy_sync::mutex::Mutex;
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel};
use embassy_time::{Duration, Ticker, Timer};
use embedded_hal::digital::v2::{InputPin, OutputPin};
Expand All @@ -27,7 +27,7 @@ use usbd_human_interface_device::{
pub use usbd_human_interface_device::page::Consumer;

use crate::hw::platform::RawMutex;
use crate::hw::CURRENT_OUTPUT_STATE;
use crate::hw::{HIDDevice, CURRENT_OUTPUT_STATE};

pub use rumcake_macros::{
build_analog_matrix, build_direct_pin_matrix, build_layout, build_standard_matrix, remap_matrix,
Expand Down Expand Up @@ -408,24 +408,8 @@ pub async fn matrix_poll<K: KeyboardMatrix + 'static>(_k: K) {
/// slots will be used.
pub static MATRIX_EVENTS: PubSubChannel<RawMutex, Event, 4, 4, 1> = PubSubChannel::new();

/// Channel for sending NKRO HID keyboard reports.
///
/// Channel messages should be consumed by the bluetooth task or USB task, so user-level code
/// should **not** attempt to receive messages from the channel, otherwise commands may not be
/// processed appropriately. You should only send to this channel.
pub static KEYBOARD_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, NKROBootKeyboardReport, 1> =
Channel::new();

/// Channel for sending consumer HID reports.
///
/// Channel messages should be consumed by the bluetooth task or USB task, so user-level code
/// should **not** attempt to receive messages from the channel, otherwise commands may not be
/// processed appropriately. You should only send to this channel.
pub static CONSUMER_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, MultipleConsumerReport, 1> =
Channel::new();

#[rumcake_macros::task]
pub async fn layout_collect<K: KeyboardLayout + 'static>(_k: K)
pub async fn layout_collect<K: KeyboardLayout + HIDDevice + 'static>(_k: K)
where
[(); K::LAYERS]:,
[(); K::LAYOUT_COLS]:,
Expand All @@ -438,13 +422,18 @@ where
let mut codes = [Consumer::Unassigned; 4];

let mut ticker = Ticker::every(Duration::from_millis(1));
let channel = K::get_matrix_events_channel();
let matrix_channel = K::get_matrix_events_channel();

#[cfg(feature = "media-keycodes")]
let consumer_report_channel = K::get_consumer_report_send_channel();

let keyboard_report = K::get_keyboard_report_send_channel();

loop {
let keys = {
let mut layout = layout.layout.lock().await;

if let Ok(event) = channel.try_receive() {
if let Ok(event) = matrix_channel.try_receive() {
layout.event(event);
MATRIX_EVENTS.publish_immediate(event); // Just immediately publish since we don't want to hold up any key events to be converted into keycodes.
};
Expand All @@ -466,7 +455,7 @@ where
{
*c = keycode;
}
CONSUMER_REPORT_HID_SEND_CHANNEL
consumer_report_channel
.send(MultipleConsumerReport { codes })
.await;
}
Expand Down Expand Up @@ -522,7 +511,7 @@ where
if let Some(c) = codes.iter_mut().find(|c| **c == keycode) {
*c = Consumer::Unassigned;
}
CONSUMER_REPORT_HID_SEND_CHANNEL
consumer_report_channel
.send(MultipleConsumerReport { codes })
.await;
}
Expand Down Expand Up @@ -553,7 +542,7 @@ where
// are both not connected, this channel can become filled, so we discard the report in
// that case.
if CURRENT_OUTPUT_STATE.get().await.is_some() {
KEYBOARD_REPORT_HID_SEND_CHANNEL
keyboard_report
.send(NKROBootKeyboardReport::new(keys))
.await;
} else {
Expand Down
Loading

0 comments on commit 297ae98

Please sign in to comment.