Skip to content

Commit 25bb721

Browse files
authored
Merge pull request #110 from RobertZ2011/keyboard-service
First keyboard service commits
2 parents c1c6701 + ac096eb commit 25bb721

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

embedded-service/src/keyboard.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//! Keyboard service data types and common functionality
2+
use core::cell::Cell;
3+
4+
use embassy_sync::once_lock::OnceLock;
5+
6+
use crate::buffer::SharedRef;
7+
use crate::comms::{self, EndpointID, External, Internal};
8+
9+
/// Keyboard device ID
10+
#[derive(Debug, Clone, Copy)]
11+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12+
pub struct DeviceId(pub u8);
13+
14+
/// Keyboard key
15+
#[derive(Debug, Clone, Copy)]
16+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17+
pub struct Key(pub u8);
18+
19+
/// Key event data
20+
#[derive(Debug, Clone, Copy)]
21+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22+
pub enum KeyEvent {
23+
/// Key release
24+
Break(Key),
25+
/// Key press
26+
Make(Key),
27+
}
28+
29+
/// Keyboard event messages
30+
#[derive(Clone)]
31+
pub enum Event<'a> {
32+
/// Key press event
33+
KeyEvent(DeviceId, SharedRef<'a, KeyEvent>),
34+
}
35+
36+
/// Top-level message data enum
37+
#[derive(Clone)]
38+
pub enum MessageData<'a> {
39+
/// Event
40+
Event(Event<'a>),
41+
}
42+
43+
/// Top-level message struct
44+
#[derive(Clone)]
45+
pub struct Message<'a> {
46+
/// Target/source device ID
47+
pub device_id: DeviceId,
48+
/// Message data
49+
pub data: MessageData<'a>,
50+
}
51+
52+
/// Broadcast target configuration
53+
#[derive(Copy, Clone, Default)]
54+
pub struct BroadcastConfig {
55+
/// Enable broadcasting to the HID endpoint
56+
broadcast_hid: bool,
57+
/// Enable broadcasting to the host endpoint
58+
broadcast_host: bool,
59+
}
60+
61+
/// Keyboard service context
62+
struct Context {
63+
broadcast_config: Cell<BroadcastConfig>,
64+
}
65+
66+
static CONTEXT: OnceLock<Context> = OnceLock::new();
67+
68+
/// Initialize common keyboard service functionality
69+
pub fn init() {
70+
CONTEXT.get_or_init(|| Context {
71+
broadcast_config: Cell::new(BroadcastConfig::default()),
72+
});
73+
}
74+
75+
/// Enable broadcasting messages to the host endpoint
76+
pub async fn enable_broadcast_host() {
77+
let context = CONTEXT.get().await;
78+
79+
let mut config = context.broadcast_config.get();
80+
config.broadcast_host = true;
81+
context.broadcast_config.set(config);
82+
}
83+
84+
/// Enable broadcasting messages to the HID endpoint
85+
pub async fn enable_broadcast_hid() {
86+
let context = CONTEXT.get().await;
87+
88+
let mut config = context.broadcast_config.get();
89+
config.broadcast_hid = true;
90+
context.broadcast_config.set(config);
91+
}
92+
93+
/// Broadcast a keyboard message to the specified endpoints
94+
pub async fn broadcast_message_with_config(from: DeviceId, config: BroadcastConfig, data: MessageData<'static>) {
95+
let message = Message { device_id: from, data };
96+
97+
if config.broadcast_hid {
98+
let _ = comms::send(
99+
EndpointID::Internal(Internal::Keyboard),
100+
EndpointID::Internal(Internal::Hid),
101+
&message,
102+
)
103+
.await;
104+
}
105+
106+
if config.broadcast_host {
107+
let _ = comms::send(
108+
EndpointID::Internal(Internal::Keyboard),
109+
EndpointID::External(External::Host),
110+
&message,
111+
)
112+
.await;
113+
}
114+
}
115+
116+
/// Broadcast a keyboard message using the global broadcast config
117+
pub async fn broadcast_message(from: DeviceId, data: MessageData<'static>) {
118+
let config = CONTEXT.get().await.broadcast_config.get();
119+
broadcast_message_with_config(from, config, data).await;
120+
}

embedded-service/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ pub mod buffer;
1212
pub mod comms;
1313
pub mod fmt;
1414
pub mod hid;
15+
pub mod keyboard;
1516
pub mod type_c;
1617

1718
/// initialize all service static interfaces as required. Ideally, this is done before subsystem initialization
1819
pub async fn init() {
1920
comms::init();
2021
activity::init();
2122
hid::init();
23+
keyboard::init();
2224
}

examples/std/src/bin/keyboard.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use embassy_executor::{Executor, Spawner};
2+
use embassy_sync::once_lock::OnceLock;
3+
use embassy_time::Timer;
4+
use embedded_services::keyboard::{self, DeviceId, Key, KeyEvent};
5+
use embedded_services::{comms, define_static_buffer};
6+
use log::info;
7+
use static_cell::StaticCell;
8+
9+
/// Mock that generates keyboard messages
10+
mod device {
11+
use std::borrow::BorrowMut;
12+
13+
use embedded_services::buffer::OwnedRef;
14+
use embedded_services::keyboard::{self, DeviceId, Event, Key, KeyEvent, MessageData};
15+
16+
pub struct Device {
17+
id: DeviceId,
18+
event_buffer: OwnedRef<'static, KeyEvent>,
19+
}
20+
21+
impl Device {
22+
pub fn new(id: DeviceId, event_buffer: OwnedRef<'static, KeyEvent>) -> Self {
23+
Self { id, event_buffer }
24+
}
25+
26+
pub async fn key_down(&self, key: Key) {
27+
{
28+
let mut borrow = self.event_buffer.borrow_mut();
29+
let buf: &mut [KeyEvent] = borrow.borrow_mut();
30+
31+
buf[0] = KeyEvent::Make(key);
32+
}
33+
34+
keyboard::broadcast_message(
35+
self.id,
36+
MessageData::Event(Event::KeyEvent(self.id, self.event_buffer.reference().slice(0..1))),
37+
)
38+
.await;
39+
}
40+
41+
pub async fn key_up(&self, key: Key) {
42+
{
43+
let mut borrow = self.event_buffer.borrow_mut();
44+
let buf: &mut [KeyEvent] = borrow.borrow_mut();
45+
46+
buf[0] = KeyEvent::Break(key);
47+
}
48+
49+
keyboard::broadcast_message(
50+
self.id,
51+
MessageData::Event(Event::KeyEvent(self.id, self.event_buffer.reference().slice(0..1))),
52+
)
53+
.await;
54+
}
55+
}
56+
}
57+
58+
/// Mock host device
59+
mod host {
60+
use std::borrow::Borrow;
61+
62+
use embedded_services::comms::{self, Endpoint, EndpointID, External, MailboxDelegate};
63+
use embedded_services::keyboard::{Event, KeyEvent, Message, MessageData};
64+
use log::info;
65+
66+
pub struct Host {
67+
pub tp: Endpoint,
68+
}
69+
70+
impl Host {
71+
pub fn new() -> Self {
72+
Self {
73+
tp: Endpoint::uninit(EndpointID::External(External::Host)),
74+
}
75+
}
76+
}
77+
78+
impl MailboxDelegate for Host {
79+
fn receive(&self, message: &comms::Message) {
80+
if let Some(message) = message.data.get::<Message>() {
81+
match &message.data {
82+
MessageData::Event(Event::KeyEvent(id, events)) => {
83+
let borrow = events.borrow();
84+
let buf: &[KeyEvent] = borrow.borrow();
85+
86+
for event in buf {
87+
info!("Host received event from device {}: {:?}", id.0, event);
88+
}
89+
}
90+
}
91+
}
92+
}
93+
}
94+
}
95+
96+
const DEVICE0_ID: DeviceId = DeviceId(0);
97+
98+
#[embassy_executor::task]
99+
async fn device() {
100+
define_static_buffer!(buffer, KeyEvent, [KeyEvent::Break(Key(0)); 8]);
101+
102+
info!("Device task");
103+
static DEVICE0: OnceLock<device::Device> = OnceLock::new();
104+
let this = DEVICE0.get_or_init(|| device::Device::new(DEVICE0_ID, buffer::get_mut().unwrap()));
105+
info!("Registering device 0 endpoint");
106+
107+
loop {
108+
info!("Sending message");
109+
this.key_down(Key(0x04)).await;
110+
Timer::after_millis(250).await;
111+
this.key_up(Key(0x04)).await;
112+
Timer::after_secs(1).await;
113+
}
114+
}
115+
116+
#[embassy_executor::task]
117+
async fn host() {
118+
info!("Host task");
119+
static HOST: OnceLock<host::Host> = OnceLock::new();
120+
let this = HOST.get_or_init(|| host::Host::new());
121+
info!("Registering host endpoint");
122+
comms::register_endpoint(this, &this.tp).await.unwrap();
123+
}
124+
125+
#[embassy_executor::task]
126+
async fn run(spawner: Spawner) {
127+
embedded_services::init().await;
128+
129+
keyboard::enable_broadcast_host().await;
130+
131+
spawner.must_spawn(host());
132+
spawner.must_spawn(device());
133+
}
134+
135+
fn main() {
136+
env_logger::builder().filter_level(log::LevelFilter::Info).init();
137+
138+
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
139+
let executor = EXECUTOR.init(Executor::new());
140+
executor.run(|spawner| {
141+
spawner.must_spawn(run(spawner));
142+
});
143+
}

0 commit comments

Comments
 (0)