Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions rmk-macro/src/codegen/chip/bind_interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ pub(crate) fn bind_interrupt_default(hardware: &Hardware, item_mod: &ItemMod) ->
quote! { CLOCK_POWER => ::nrf_sdc::mpsl::ClockInterruptHandler; }
};

let support_subrating = if is_feature_enabled(&get_rmk_features(), "subrating") {
quote! { .support_connection_subrating_central() }
} else {
quote! {}
};

let ble_config = communication.get_ble_config().unwrap();
let tx_power = if let Some(pwr) = ble_config.default_tx_power {
quote! { .default_tx_power(#pwr)? }
Expand All @@ -201,6 +207,7 @@ pub(crate) fn bind_interrupt_default(hardware: &Hardware, item_mod: &ItemMod) ->
.support_dle_central()
.support_phy_update_central()
.support_phy_update_peripheral()
#support_subrating
#use_2m_phy
#tx_power
.central_count(#num_peri)?
Expand Down
9 changes: 8 additions & 1 deletion rmk-macro/src/codegen/chip/chip_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rmk_config::resolved::Hardware;
use rmk_config::resolved::hardware::{BoardConfig, ChipModel, ChipSeries, CommunicationConfig};
use syn::{ItemFn, ItemMod};

use crate::codegen::feature::{get_rmk_features, is_feature_enabled};
use crate::codegen::override_helper::{Overwritten, find_overwritten};

/// Expand chip initialization code
Expand Down Expand Up @@ -94,7 +95,13 @@ pub(crate) fn chip_init_default(hardware: &Hardware, peripheral_id: Option<usize
// Unibody: 4696. Split central: 6080 + (N-1) * 2288 per peripheral.
let sdc_mem_size = if peripheral_id.is_none() {
if peri_num > 0 {
6080 + (peri_num.saturating_sub(1)) * 2288
// Subrating needs extra memory
// TODO: Check how much exactly
if is_feature_enabled(&get_rmk_features(), "subrating") {
6400 + (peri_num.saturating_sub(1)) * 2288
} else {
6080 + (peri_num.saturating_sub(1)) * 2288
}
} else {
4696
}
Expand Down
7 changes: 7 additions & 0 deletions rmk-macro/src/codegen/split/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ fn expand_bind_interrupt_for_split_peripheral(
quote! {}
};

let support_subrating = if is_feature_enabled(&get_rmk_features(), "subrating") {
quote! { .support_connection_subrating_peripheral() }
} else {
quote! {}
};

let ble_config = communication.get_ble_config().unwrap();
let tx_power = if let Some(pwr) = ble_config.default_tx_power {
quote! { .default_tx_power(#pwr)? }
Expand Down Expand Up @@ -258,6 +264,7 @@ fn expand_bind_interrupt_for_split_peripheral(
.support_dle_central()
.support_phy_update_central()
.support_phy_update_peripheral()
#support_subrating
#use_2m_phy
#tx_power
.peripheral_count(1)?
Expand Down
7 changes: 6 additions & 1 deletion rmk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ _no_usb = []
## Internal marker for 480 Mbps USB chips; activates 512-byte bulk packets.
_usb_high_speed = []

## Enable BLE connection subrating for split Connections
subrating = ["_ble"]
## Internal feature that indicates that subrating is not supported, this will be auto-activated for some chips
_no_subrating = []

#! ### BLE feature flags
#!
#! ⚠️ Due to the limitation of docs.rs, functions gated by BLE features won't show in docs.rs. You have to head to [`examples`](https://github.com/rmk-rs/rmk/tree/main/examples) folder of RMK repo for their usages.
Expand Down Expand Up @@ -282,7 +287,7 @@ esp32s3_ble = ["_esp_ble"]
_esp_ble = ["_ble", "dep:esp-hal"]

## Enable feature if you want to use RP2040W with BLE.
pico_w_ble = ["_ble", "dep:embassy-rp"]
pico_w_ble = ["_ble", "_no_subrating", "dep:embassy-rp"]

## Enable feature if you want to use SF32LB52x with BLE.
sf32lb52x_ble = ["_ble"]
Expand Down
97 changes: 94 additions & 3 deletions rmk/src/ble/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use bt_hci::cmd::le::{LeReadLocalSupportedFeatures, LeSetPhy};
use bt_hci::cmd::le::{LeReadLocalSupportedFeatures, LeSetHostFeature, LeSetPhy};
#[cfg(feature = "subrating")]
use bt_hci::cmd::le::{LeSubrateRequest, LeSubrateRequestParams};
use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
use embassy_futures::join::join3;
use embassy_futures::select::{Either, Either3, select, select3};
Expand Down Expand Up @@ -39,6 +41,9 @@ pub mod passkey;
pub(crate) mod profile;
pub(crate) mod sleep;

#[cfg(all(feature = "subrating", feature = "_no_subrating"))]
compile_error!("You may not enable feature `subrating` on unsupported platforms!");

/// Max number of connections
pub(crate) const CONNECTIONS_MAX: usize = crate::SPLIT_PERIPHERALS_NUM + 1;

Expand Down Expand Up @@ -152,7 +157,10 @@ where
impl<'a, 'b, 's, C> Runnable for BleTransport<'a, 'b, 's, C>
where
's: 'b,
C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
C: Controller
+ ControllerCmdAsync<LeSetPhy>
+ ControllerCmdSync<LeReadLocalSupportedFeatures>
+ ControllerCmdSync<LeSetHostFeature>,
{
async fn run(&mut self) -> ! {
// Load the preferred connection from storage
Expand All @@ -173,6 +181,20 @@ where
let product_name = self.product_name;

let connection_loop = async {
// Set subrating host support feature flag
#[cfg(feature = "subrating")]
{
const CONN_SUBRATING_HOST_BIT: u8 = 38;
let cmd = LeSetHostFeature::new(CONN_SUBRATING_HOST_BIT, 1);
if let Err(e) = stack.command(cmd).await {
error!("error setting host feature: {:?}", e);
}
}

#[cfg(feature = "split")]
// Signal to indicate the stack is started
crate::split::ble::central::STACK_STARTED.signal(true);

loop {
match select(
advertise(product_name, &mut peripheral, server),
Expand Down Expand Up @@ -284,6 +306,34 @@ pub(crate) async fn ble_task<C: Controller + ControllerCmdAsync<LeSetPhy>, P: Pa
error!("[ble_task] runner error: {:?}", e);
embassy_time::Timer::after_millis(100).await;
}
<<<<<<< HEAD
||||||| parent of 31ec794c (Enable Bluetooth LE Connection Subrating for split communication)

#[cfg(feature = "split")]
{
// Signal to indicate the stack is started
crate::split::ble::central::STACK_STARTED.signal(true);
if let Err(_e) = runner
.run_with_handler(&crate::split::ble::central::ScanHandler {})
.await
{
error!("[ble_task] runner.run_with_handler error");
embassy_time::Timer::after_millis(100).await;
}
}
=======

#[cfg(feature = "split")]
{
if let Err(_e) = runner
.run_with_handler(&crate::split::ble::central::ScanHandler {})
.await
{
error!("[ble_task] runner.run_with_handler error");
embassy_time::Timer::after_millis(100).await;
}
}
>>>>>>> 31ec794c (Enable Bluetooth LE Connection Subrating for split communication)
}
}

Expand Down Expand Up @@ -643,7 +693,7 @@ pub(crate) async fn set_conn_params<
&RequestedConnParams {
min_connection_interval: Duration::from_micros(7500),
max_connection_interval: Duration::from_micros(7500),
max_latency: 30,
max_latency: 300, // let central sleep and save power
min_event_length: Duration::from_secs(0),
max_event_length: Duration::from_secs(0),
supervision_timeout: Duration::from_secs(10),
Expand Down Expand Up @@ -813,6 +863,47 @@ pub(crate) async fn update_conn_params<
false
}

/// Update the subrate factor.
///
/// Returns whether the request reached the controller, so callers that mirror
/// the parameters in their own state don't record params that never landed.
#[cfg(feature = "subrating")]
pub(crate) async fn update_subrate_factor<
'a,
'b,
C: Controller + ControllerCmdAsync<LeSubrateRequest>,
P: PacketPool,
>(
stack: &Stack<'a, C, P>,
params: LeSubrateRequestParams,
) -> bool {
for _ in 0..10 {
let subrate_request = LeSubrateRequest::from(params);

match stack.async_command(subrate_request).await {
Ok(_) => return true,
Err(BleHostError::BleHost(Error::Hci(error))) => {
if 0x3A == error.to_status().into_inner() {
// Busy, retry
info!("[update_subrate_factor] HCI busy: {:?}", error);
embassy_time::Timer::after_millis(100).await;
continue;
}
error!("[update_subrate_factor] HCI error: {:?}", error);
return false;
}
Err(e) => {
#[cfg(feature = "defmt")]
let e = defmt::Debug2Format(&e);
error!("[update_subrate_factor] BLE host error: {:?}", e);
return false;
}
}
}
warn!("[update_conn_params] controller stayed busy, giving up");
false
}

#[cfg(test)]
mod tests {
use std::sync::{Mutex, OnceLock};
Expand Down
83 changes: 68 additions & 15 deletions rmk/src/split/ble/central.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use core::cell::RefCell;

use bt_hci::cmd::le::{LeReadLocalSupportedFeatures, LeSetPhy, LeSetScanParams};
#[cfg(feature = "subrating")]
use bt_hci::cmd::le::{LeSubrateRequest, LeSubrateRequestParams};
use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
#[cfg(feature = "subrating")]
use bt_hci::param::ConnHandle;
use embassy_futures::select::{Either, Either3, select, select3};
use embassy_sync::mutex::Mutex;
use embassy_sync::signal::Signal;
Expand All @@ -11,6 +15,8 @@ use trouble_host::prelude::*;

use super::GattSplitMessage;
use crate::ble::sleep::report_activity;
#[cfg(feature = "subrating")]
use crate::ble::update_subrate_factor;
use crate::ble::{update_ble_phy, update_conn_params};
use crate::channel::FLASH_CHANNEL;
use crate::event::{EventSubscriber, SleepStateEvent, SubscribableEvent};
Expand Down Expand Up @@ -47,10 +53,7 @@ struct BleSplitCentralServer {
pub async fn scan_peripherals<
'b,
's: 'b,
C: Controller
+ ControllerCmdSync<LeSetScanParams>
+ ControllerCmdAsync<LeSetPhy>
+ ControllerCmdSync<LeReadLocalSupportedFeatures>,
C: Controller + ControllerCmdSync<LeSetScanParams> + ControllerCmdAsync<LeSetPhy>,
>(
stack: &'b Stack<'s, C, DefaultPacketPool>,
addrs: &RefCell<VecView<Option<[u8; 6]>>>,
Expand Down Expand Up @@ -157,10 +160,15 @@ impl EventHandler for ScanHandler {
pub(crate) async fn run_ble_peripheral_manager<
'b,
's: 'b,
C: Controller
#[cfg(not(feature = "subrating"))] C: Controller
+ ControllerCmdSync<LeSetScanParams>
+ ControllerCmdAsync<LeSetPhy>
+ ControllerCmdSync<LeReadLocalSupportedFeatures>,
#[cfg(feature = "subrating")] C: Controller
+ ControllerCmdSync<LeSetScanParams>
+ ControllerCmdAsync<LeSetPhy>
+ ControllerCmdSync<LeReadLocalSupportedFeatures>
+ ControllerCmdAsync<LeSubrateRequest>,
const ROW: usize,
const COL: usize,
const ROW_OFFSET: usize,
Expand Down Expand Up @@ -252,12 +260,24 @@ fn default_central_conn_param() -> RequestedConnParams {
RequestedConnParams {
min_connection_interval: Duration::from_micros(7500),
max_connection_interval: Duration::from_micros(7500),
max_latency: 10, // 75ms
max_latency: 300, // 2250ms
supervision_timeout: Duration::from_secs(10),
..Default::default()
}
}

#[cfg(feature = "subrating")]
fn default_central_subrate_params(handle: ConnHandle) -> LeSubrateRequestParams {
LeSubrateRequestParams {
handle,
subrate_min: 1,
subrate_max: 1,
max_latency: 300, // 2250ms
continuation_number: 0,
supervision_timeout: ::bt_hci::param::Duration::from_secs(10),
}
}

/// Parameters for the central -> peripheral link while the central sleeps.
///
/// With a host connected, the central's radio is busy serving the host link
Expand All @@ -284,10 +304,26 @@ fn sleep_central_conn_param() -> RequestedConnParams {
}
}

#[cfg(feature = "subrating")]
fn sleep_central_subrate_params(handle: ConnHandle) -> LeSubrateRequestParams {
LeSubrateRequestParams {
handle,
subrate_min: 60, // 450ms interval -> 457.5ms key press latency
subrate_max: 60,
max_latency: 7, // 3,6s sleep for peripheral
continuation_number: 2, // -> assure low latency reset of subrate factor.
supervision_timeout: ::bt_hci::param::Duration::from_secs(8),
}
}

async fn run_central_manager_task<
'b,
's: 'b,
C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
#[cfg(not(feature = "subrating"))] C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
#[cfg(feature = "subrating")] C: Controller
+ ControllerCmdAsync<LeSetPhy>
+ ControllerCmdSync<LeReadLocalSupportedFeatures>
+ ControllerCmdAsync<LeSubrateRequest>,
P: PacketPool,
const ROW: usize,
const COL: usize,
Expand Down Expand Up @@ -479,7 +515,8 @@ pub(crate) async fn wait_for_stack_started() {
async fn follow_sleep_state<
'b,
's: 'b,
C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
#[cfg(not(feature = "subrating"))] C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
#[cfg(feature = "subrating")] C: Controller + ControllerCmdAsync<LeSetPhy> + ControllerCmdAsync<LeSubrateRequest>,
P: PacketPool,
>(
stack: &'b Stack<'s, C, P>,
Expand All @@ -502,13 +539,29 @@ async fn follow_sleep_state<
if sleeping == applied {
continue;
}
let params = if sleeping {
sleep_central_conn_param()
} else {
default_central_conn_param()
};
if update_conn_params(stack, conn, &params).await {
applied = sleeping;
#[cfg(not(feature = "subrating"))]
{
let params = if sleeping {
sleep_central_conn_param()
} else {
default_central_conn_param()
};
if update_conn_params(stack, conn, &params).await {
applied = sleeping;
}
}

#[cfg(feature = "subrating")]
{
let params = if sleeping {
sleep_central_subrate_params(conn.handle())
} else {
default_central_subrate_params(conn.handle())
};

if update_subrate_factor(stack, params).await {
applied = sleeping;
}
}
}
}
Loading
Loading