use core::future::Future;
use embassy_futures::select::select;
use embassy_time::{Instant, Timer};
use session::{BTP_ACK_TIMEOUT_SECS, BTP_CONN_IDLE_TIMEOUT_SECS};
use crate::error::{Error, ErrorCode};
use crate::transport::network::btp::session::Session;
use crate::transport::network::{Address, BtAddr, NetworkReceive, NetworkSend, MAX_TX_PACKET_SIZE};
use crate::utils::cell::RefCell;
use crate::utils::init::{init, Init};
use crate::utils::storage::Vec;
use crate::utils::sync::blocking::Mutex;
use crate::utils::sync::Notification;
pub use gatt::*;
mod gatt;
mod session;
pub(crate) const MAX_BTP_SEGMENT_SIZE: usize = 244;
pub(crate) const GATT_HEADER_SIZE: usize = 3;
pub(crate) const MIN_MTU: u16 = (20 + GATT_HEADER_SIZE) as u16;
pub(crate) const MAX_MTU: u16 = (MAX_BTP_SEGMENT_SIZE + GATT_HEADER_SIZE) as u16;
pub struct Btp {
inner: Mutex<RefCell<BtpInner>>,
recv_notif: Notification,
send_notif: Notification,
outg_notif: Notification,
}
impl Btp {
#[inline(always)]
pub const fn new() -> Self {
Self {
inner: Mutex::new(RefCell::new(BtpInner::new())),
recv_notif: Notification::new(),
send_notif: Notification::new(),
outg_notif: Notification::new(),
}
}
pub fn init() -> impl Init<Self> {
init!(Self {
inner <- Mutex::init(RefCell::init(BtpInner::init())),
recv_notif: Notification::new(),
send_notif: Notification::new(),
outg_notif: Notification::new(),
})
}
pub fn reset(&self) {
self.inner.lock(|inner| {
inner.borrow_mut().reset();
self.recv_notif.notify();
self.send_notif.notify();
self.outg_notif.notify();
});
}
pub fn set_relaxed_mtu_nego(&self, relaxed_mtu_nego: bool) {
self.inner
.lock(|inner| inner.borrow_mut().set_relaxed_mtu_nego(relaxed_mtu_nego));
}
#[allow(dead_code)]
pub(crate) fn set_timeouts(&self, ack_timeout_secs: u8, conn_idle_timeout_secs: u8) {
self.inner.lock(|inner| {
inner
.borrow_mut()
.set_timeouts(ack_timeout_secs, conn_idle_timeout_secs);
self.recv_notif.notify();
self.send_notif.notify();
self.outg_notif.notify();
});
}
pub fn timeout(&self) -> bool {
self.inner.lock(|inner| inner.borrow().timeout())
}
pub async fn wait_timeout(&self) {
const TIMEOUT_CHECK_SECS: u64 = 2;
while !self.timeout() {
Timer::after_secs(TIMEOUT_CHECK_SECS).await;
}
}
pub fn process_incoming(
&self,
gatt_mtu: Option<u16>,
addr: BtAddr,
data: &[u8],
) -> Result<(), Error> {
self.inner.lock(|inner| {
inner.borrow_mut().process_incoming(gatt_mtu, addr, data)?;
self.recv_notif.notify();
self.outg_notif.notify();
Ok(())
})
}
pub fn process_outgoing(&self, gatt_mtu: Option<u16>, buf: &mut [u8]) -> Result<usize, Error> {
self.inner.lock(|inner| {
let mut inner = inner.borrow_mut();
let len = inner.process_outgoing(gatt_mtu, buf)?;
if inner.outgoing_sdu.buf.is_empty() {
self.send_notif.notify();
}
Ok(len)
})
}
pub async fn wait_outgoing(&self) {
const ACK_TIMEOUT_CHECK_SECS: u64 = 1;
select(
self.outg_notif.wait(),
Timer::after_secs(ACK_TIMEOUT_CHECK_SECS),
)
.await;
}
pub async fn wait_available(&self) -> Result<(), Error> {
loop {
let available = self.inner.lock(|inner| inner.borrow().available());
if available {
break;
}
self.recv_notif.wait().await;
}
Ok(())
}
pub async fn recv(&self, buf: &mut [u8]) -> Result<(usize, BtAddr), Error> {
loop {
let result = self.inner.lock(|inner| {
let result = inner.borrow_mut().recv(buf)?;
if result.is_some() {
self.outg_notif.notify();
}
Ok::<_, Error>(result)
})?;
if let Some(result) = result {
break Ok(result);
}
self.recv_notif.wait().await;
}
}
pub async fn send(&self, data: &[u8], addr: BtAddr) -> Result<(), Error> {
loop {
let sent = self.inner.lock(|inner| {
let sent = inner.borrow_mut().send(data, addr)?;
if sent {
self.outg_notif.notify();
}
Ok::<_, Error>(sent)
})?;
if sent {
break Ok(());
}
self.send_notif.wait().await;
}
}
}
impl Default for Btp {
fn default() -> Self {
Self::new()
}
}
impl NetworkSend for &Btp {
async fn send_to(&mut self, data: &[u8], addr: Address) -> Result<(), Error> {
(*self)
.send(data, addr.btp().ok_or(ErrorCode::NoNetworkInterface)?)
.await
}
}
impl NetworkReceive for &Btp {
fn wait_available(&mut self) -> impl Future<Output = Result<(), Error>> {
(*self).wait_available()
}
async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Address), Error> {
(*self)
.recv(buffer)
.await
.map(|(len, addr)| (len, Address::Btp(addr)))
}
}
impl NetworkSend for Btp {
async fn send_to(&mut self, data: &[u8], addr: Address) -> Result<(), Error> {
(&*self).send_to(data, addr).await
}
}
impl NetworkReceive for Btp {
fn wait_available(&mut self) -> impl Future<Output = Result<(), Error>> {
(*self).wait_available()
}
async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Address), Error> {
(&*self).recv_from(buffer).await
}
}
struct BtpInner {
session: Session,
outgoing_sdu: OutgoingSdu,
ack_timeout_secs: u8,
conn_idle_timeout_secs: u8,
}
impl BtpInner {
const fn new() -> Self {
Self {
session: Session::new(),
outgoing_sdu: OutgoingSdu::new(),
ack_timeout_secs: BTP_ACK_TIMEOUT_SECS,
conn_idle_timeout_secs: BTP_CONN_IDLE_TIMEOUT_SECS,
}
}
fn init() -> impl Init<Self> {
init!(Self {
session <- Session::init(),
outgoing_sdu <- OutgoingSdu::init(),
ack_timeout_secs: BTP_ACK_TIMEOUT_SECS,
conn_idle_timeout_secs: BTP_CONN_IDLE_TIMEOUT_SECS,
})
}
fn reset(&mut self) {
self.session.reset();
self.outgoing_sdu.reset();
self.ack_timeout_secs = BTP_ACK_TIMEOUT_SECS;
self.conn_idle_timeout_secs = BTP_CONN_IDLE_TIMEOUT_SECS;
}
fn set_relaxed_mtu_nego(&mut self, relaxed_mtu_nego: bool) {
self.session.set_relaxed_mtu_nego(relaxed_mtu_nego);
}
fn set_timeouts(&mut self, ack_timeout_secs: u8, conn_idle_timeout_secs: u8) {
self.ack_timeout_secs = ack_timeout_secs;
self.conn_idle_timeout_secs = conn_idle_timeout_secs;
}
fn process_incoming(
&mut self,
gatt_mtu: Option<u16>,
addr: BtAddr,
data: &[u8],
) -> Result<(), Error> {
self.session.process_rx(gatt_mtu, addr, data)
}
fn process_outgoing(&mut self, gatt_mtu: Option<u16>, buf: &mut [u8]) -> Result<usize, Error> {
let len = self.session.prep_tx_handshake(gatt_mtu, buf)?;
if len > 0 {
return Ok(len);
}
if !self.outgoing_sdu.buf.is_empty() {
if self.outgoing_sdu.address == self.session.address() {
let len = self.session.prep_tx_data(
&self.outgoing_sdu.buf,
&mut self.outgoing_sdu.buf_offset,
buf,
)?;
if len > 0 {
if self.outgoing_sdu.buf_offset == self.outgoing_sdu.buf.len() {
self.outgoing_sdu.reset();
}
return Ok(len);
}
} else {
self.outgoing_sdu.reset();
}
}
if self
.session
.is_ack_due(Instant::now(), self.ack_timeout_secs as _)
{
let len = self.session.prep_tx_data(&[], &mut 0, buf)?;
assert!(len > 0);
return Ok(len);
}
Ok(0)
}
fn available(&self) -> bool {
self.session.message_available()
}
fn recv(&mut self, buf: &mut [u8]) -> Result<Option<(usize, BtAddr)>, Error> {
if self.session.message_available() {
let len = self.session.fetch_message(buf)?;
Ok(Some((len, self.session.address())))
} else {
Ok(None)
}
}
fn send(&mut self, data: &[u8], addr: BtAddr) -> Result<bool, Error> {
if data.is_empty() || data.len() > MAX_TX_PACKET_SIZE {
Err(ErrorCode::InvalidArgument.into())
} else if self.outgoing_sdu.buf.is_empty() {
self.outgoing_sdu.address = addr;
self.outgoing_sdu.buf_offset = 0;
unwrap!(self.outgoing_sdu.buf.extend_from_slice(data));
Ok(true)
} else {
Ok(false)
}
}
fn timeout(&self) -> bool {
self.session
.is_timed_out(Instant::now(), self.conn_idle_timeout_secs as _)
}
}
struct OutgoingSdu {
address: BtAddr,
buf: Vec<u8, MAX_TX_PACKET_SIZE>,
buf_offset: usize,
}
impl OutgoingSdu {
const fn new() -> Self {
Self {
address: BtAddr([0; 6]),
buf: Vec::new(),
buf_offset: 0,
}
}
fn init() -> impl Init<Self> {
init!(Self {
address: BtAddr([0; 6]),
buf <- crate::utils::storage::Vec::init(),
buf_offset: 0,
})
}
fn reset(&mut self) {
self.address = BtAddr([0; 6]);
self.buf.clear();
self.buf_offset = 0;
}
}
#[cfg(test)]
mod test {
use super::*;
const PEER_ADDR: BtAddr = BtAddr([1, 2, 3, 4, 5, 6]);
fn incoming(btp: &Btp, data: &[u8]) {
incoming_mtu(btp, None, data)
}
fn expect_outgoing(btp: &Btp, data: &[u8]) {
expect_outgoing_mtu(btp, None, data)
}
fn incoming_mtu(btp: &Btp, gatt_mtu: Option<u16>, data: &[u8]) {
btp.process_incoming(gatt_mtu, PEER_ADDR, data).unwrap();
}
fn expect_outgoing_mtu(btp: &Btp, gatt_mtu: Option<u16>, data: &[u8]) {
let mut buf = [0; 512];
let len = btp.process_outgoing(gatt_mtu, &mut buf).unwrap();
assert_eq!(&buf[..len], data);
}
fn send(btp: &Btp, data: &[u8]) {
embassy_futures::block_on(btp.send(data, PEER_ADDR)).unwrap();
}
fn expect_recv(btp: &Btp, data: &[u8]) {
let mut buf = [0; 2048];
let (len, addr) = embassy_futures::block_on(btp.recv(&mut buf)).unwrap();
assert_eq!(addr, PEER_ADDR);
assert_eq!(&buf[..len], data);
}
#[test]
fn test_mtu_timeout() {
#[cfg(all(feature = "std", not(target_os = "espidf")))]
{
let _ = env_logger::try_init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);
}
let btp = Btp::new();
btp.set_timeouts(1, 2);
incoming_mtu(
&btp,
Some(0xc8),
&[0x65, 0x6c, 0x54, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x05],
);
expect_outgoing(&btp, &[0x65, 0x6c, 0x05, 0xc5, 0x00, 0x05]);
embassy_futures::block_on(Timer::after_secs(3));
assert!(btp.timeout());
btp.reset();
incoming_mtu(
&btp,
None,
&[0x65, 0x6c, 0x54, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x05],
);
expect_outgoing(&btp, &[0x65, 0x6c, 0x05, 0x14, 0x00, 0x05]);
}
fn nego_min_mtu() -> Btp {
let btp = Btp::new();
incoming(
&btp,
&[0x65, 0x6c, 0x54, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x05],
);
expect_outgoing(&btp, &[0x65, 0x6c, 0x05, 0x14, 0x00, 0x05]);
btp
}
#[test]
fn test_short_read() {
let btp = nego_min_mtu();
send(&btp, &[0, 1, 2, 3]);
expect_outgoing(&btp, &[5, 1, 4, 0, 0, 1, 2, 3]);
}
#[test]
fn test_short_write() {
let btp = nego_min_mtu();
incoming(&btp, &[5, 0, 3, 0, 1, 2, 3]);
expect_recv(&btp, &[1, 2, 3]);
}
#[test]
fn test_long_read() {
let btp = nego_min_mtu();
send(&btp, &[0; 52]);
expect_outgoing(
&btp,
&[1, 1, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
expect_outgoing(
&btp,
&[2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
expect_outgoing(
&btp,
&[6, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
}
#[test]
fn test_long_write() {
let btp = nego_min_mtu();
incoming(
&btp,
&[
1, 0, 30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
],
);
incoming(
&btp,
&[4, 1, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
);
expect_recv(
&btp,
&[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30,
],
);
}
#[test]
fn test_long_read_ack() {
let btp = nego_min_mtu();
send(&btp, &[0, 1, 2, 3]);
expect_outgoing(&btp, &[5, 1, 4, 0, 0, 1, 2, 3]);
send(&btp, &[0; 100]);
expect_outgoing(
&btp,
&[1, 2, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
expect_outgoing(
&btp,
&[2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
incoming(&btp, &[8, 3, 0]);
expect_outgoing(
&btp,
&[10, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
}
#[test]
fn test_long_write_ack() {
let btp = nego_min_mtu();
incoming(
&btp,
&[1, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
incoming(
&btp,
&[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
incoming(&btp, &[4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
expect_recv(&btp, &[0; 44]);
}
#[test]
fn test_idle_ping_pong() {
let btp = nego_min_mtu();
btp.set_timeouts(1, 10);
incoming(&btp, &[8, 0, 0]);
embassy_futures::block_on(Timer::after_secs(1));
expect_outgoing(&btp, &[8, 0, 1]);
incoming(&btp, &[8, 1, 1]);
embassy_futures::block_on(Timer::after_secs(1));
expect_outgoing(&btp, &[8, 1, 2]);
}
}