#[cfg(feature = "_ble")]
#[cfg(all(feature = "_ble", feature = "subrating"))]
use bt_hci::{cmd::le::LeSetHostFeature, controller::ControllerCmdSync};
use embassy_futures::select::{Either, select};
#[cfg(not(feature = "_ble"))]
use embedded_io_async::{Read, Write};
use futures::FutureExt;
#[cfg(all(feature = "_ble", feature = "storage"))]
use {super::ble::PeerAddress, crate::channel::FLASH_CHANNEL};
#[cfg(feature = "_ble")]
use {
crate::event::{BatteryStatusEvent, ChargingStateEvent, EventSubscriber},
rmk_types::battery::BatteryStatus,
trouble_host::prelude::*,
};
use super::SplitMessage;
use super::driver::{SplitReader, SplitWriter};
use crate::event::{
KeyboardEvent, LayerChangeEvent, LedIndicatorEvent, PointingEvent, SleepStateEvent, SubscribableEvent,
publish_event,
};
#[cfg(feature = "display")]
use crate::event::{ModifierEvent, WpmUpdateEvent};
#[cfg(not(feature = "_ble"))]
use crate::split::serial::SerialSplitDriver;
use crate::state::update_status;
pub async fn run_rmk_split_peripheral<
#[cfg(all(feature = "_ble", feature = "subrating"))] C: Controller + ControllerCmdSync<LeSetHostFeature>,
#[cfg(all(feature = "_ble", not(feature = "subrating")))] C: Controller,
#[cfg(not(feature = "_ble"))] S: Write + Read,
>(
#[cfg(feature = "_ble")] id: usize,
#[cfg(feature = "_ble")] controller: C,
#[cfg(feature = "_ble")] address: [u8; 6],
#[cfg(not(feature = "_ble"))] serial: S,
) {
#[cfg(not(feature = "_ble"))]
{
let mut peripheral = SplitPeripheral::new(SerialSplitDriver::new(serial));
loop {
peripheral.run().await;
}
}
#[cfg(feature = "_ble")]
{
let mut resources: HostResources<DefaultPacketPool, 1, 4> = HostResources::new();
let stack = trouble_host::new(controller, &mut resources)
.set_random_address(Address::random(address))
.build();
crate::split::ble::peripheral::initialize_nrf_ble_split_peripheral_and_run(id, &stack).await;
}
}
pub(crate) struct SplitPeripheral<S: SplitWriter + SplitReader> {
split_driver: S,
#[cfg(feature = "dfu_split")]
dfu_handler: Option<crate::dfu::SplitDfuHandler>,
}
impl<S: SplitWriter + SplitReader> SplitPeripheral<S> {
pub(crate) fn new(split_driver: S) -> Self {
Self {
split_driver,
#[cfg(feature = "dfu_split")]
dfu_handler: None,
}
}
pub(crate) async fn run(&mut self) {
#[cfg(feature = "dfu_split")]
{
let hash = crate::dfu::read_embedded_firmware_hash();
self.split_driver
.write(&SplitMessage::FirmwareHashResponse(hash))
.await
.ok();
}
let mut key_sub = KeyboardEvent::subscriber();
#[cfg(feature = "_ble")]
let mut charging_state_sub = ChargingStateEvent::subscriber();
let mut pointing_sub = PointingEvent::subscriber();
#[cfg(feature = "_ble")]
let mut battery_sub = BatteryStatusEvent::subscriber();
loop {
let read_message_to_send = async {
crate::select_biased_with_feature! {
e = key_sub.next_message_pure().fuse() => SplitMessage::Key(e),
with_feature("_ble"): e = charging_state_sub.next_message_pure().fuse() => {
SplitMessage::BatteryStatus(BatteryStatus::Available {
charge_state: e.charging.into(),
level: None,
}.into())
},
e = pointing_sub.next_message_pure().fuse() => SplitMessage::Pointing(e),
with_feature("_ble"): e = battery_sub.next_event().fuse() => SplitMessage::BatteryStatus(e),
}
};
match select(self.split_driver.read(), read_message_to_send).await {
Either::First(m) => match m {
Ok(split_message) => match split_message {
SplitMessage::ConnectionStatus(status) => {
trace!("Received central connection status: {:?}", status);
update_status(|c| *c = status);
#[cfg(feature = "_ble")]
self.split_driver
.write(&SplitMessage::BatteryStatus(
crate::input_device::battery::current_battery_status().into(),
))
.await
.ok();
}
#[cfg(all(feature = "_ble", feature = "storage"))]
SplitMessage::ClearPeer => {
FLASH_CHANNEL
.send(crate::storage::FlashOperationMessage::PeerAddress(PeerAddress::new(
0, false, [0; 6],
)))
.await;
}
SplitMessage::KeyboardIndicator(indicator) => {
publish_event(LedIndicatorEvent::new(
rmk_types::led_indicator::LedIndicator::from_bits(indicator),
));
}
SplitMessage::Layer(layer) => {
publish_event(LayerChangeEvent::new(layer));
}
#[cfg(feature = "display")]
SplitMessage::Wpm(wpm) => publish_event(WpmUpdateEvent::new(wpm)),
#[cfg(feature = "display")]
SplitMessage::Modifier(bits) => {
publish_event(ModifierEvent {
modifier: rmk_types::modifier::ModifierCombination::from_bits(bits),
});
}
SplitMessage::SleepState(sleeping) => {
publish_event(SleepStateEvent::new(sleeping));
}
#[cfg(feature = "dfu_split")]
SplitMessage::FirmwareHashQuery => {
let hash = crate::dfu::read_embedded_firmware_hash();
info!("dfu_split: hash query, responding with {:#x}", hash);
self.split_driver
.write(&SplitMessage::FirmwareHashResponse(hash))
.await
.ok();
}
#[cfg(feature = "dfu_split")]
SplitMessage::FirmwareChunk { offset, len, data } => {
if self.dfu_handler.is_none() {
self.dfu_handler = crate::dfu::SplitDfuHandler::new();
if self.dfu_handler.is_none() {
error!("dfu_split: FlashManager not initialized, skipping chunk");
continue;
}
}
let handler = self.dfu_handler.as_mut().unwrap();
let actual_len = len as usize;
let chunk_data = &data.0[..actual_len];
match handler.write_chunk(offset as u32, chunk_data) {
Ok(()) => {
debug!("dfu_split: wrote {} bytes at offset {}", actual_len, offset);
let ack = SplitMessage::FirmwareChunkAck {
offset,
crc: crate::crc32::crc32(chunk_data),
};
self.split_driver.write(&ack).await.ok();
}
Err(()) => error!("dfu_split: write error at offset {}", offset),
}
}
#[cfg(feature = "dfu_split")]
SplitMessage::FirmwareUpdateComplete => {
if let Some(ref mut handler) = self.dfu_handler {
let dfu_crc = handler.compute_dfu_crc();
info!("dfu_split: DFU partition CRC: {:#010x}", dfu_crc);
let crc_msg = SplitMessage::FirmwareCrcReport(dfu_crc);
self.split_driver.write(&crc_msg).await.ok();
info!("dfu_split: CRC report sent");
let deadline = embassy_time::Instant::now() + embassy_time::Duration::from_secs(5);
let ok = loop {
match select(self.split_driver.read(), embassy_time::Timer::at(deadline)).await {
Either::First(Ok(SplitMessage::FirmwareCrcOk)) => {
info!("dfu_split: central confirmed CRC, resetting");
break true;
}
Either::First(Ok(SplitMessage::FirmwareCrcFail)) => {
warn!("dfu_split: central rejected CRC, stopping update");
break false;
}
Either::First(Ok(_)) => {}
Either::First(Err(e)) => {
error!("read error: {:?}", e);
break false;
}
Either::Second(_) => {
error!("timeout");
break false;
}
}
};
if ok {
self.split_driver.write(&SplitMessage::FirmwareUpdateConfirm).await.ok();
embassy_time::Timer::after_millis(50).await;
handler.mark_updated_and_reset().ok();
} else {
self.dfu_handler = None;
}
} else {
error!("dfu_split: no active DFU session");
}
}
_ => (),
},
Err(e) => {
error!("Split message read error: {:?}", e);
if let crate::split::driver::SplitDriverError::Disconnected = e {
break;
}
}
},
Either::Second(e) => {
debug!("Writing split message {:?} to central", e);
self.split_driver.write(&e).await.ok();
}
}
}
}
}