use embedded_io_async::{ErrorType, Read, Write};
use trouble_host::prelude::*;
use super::HostWriteOutcome;
use crate::ble::ble_server::Server;
use crate::channel::VIAL_BLE_RX_CHANNEL;
use crate::host::transport::HostTransportError;
use crate::host::via::VialService;
pub(crate) struct HostGattHandler {
output_handle: u16,
input_cccd_handle: u16,
control_point_handle: u16,
}
impl HostGattHandler {
pub(crate) fn new(server: &Server<'_>) -> Self {
Self {
output_handle: server.vial_service.output_data.handle,
input_cccd_handle: server
.vial_service
.input_data
.cccd_handle
.expect("No CCCD for Vial input"),
control_point_handle: server.vial_service.hid_control_point.handle,
}
}
pub(crate) async fn handle_write(&mut self, handle: u16, data: &[u8], _encrypted: bool) -> HostWriteOutcome {
if handle == self.output_handle {
if data.len() == 32 {
debug!("Got Vial packet: {:?}", data);
let mut message = [0u8; 32];
message.copy_from_slice(data);
VIAL_BLE_RX_CHANNEL.send(message).await;
} else {
warn!("Wrong Vial packet data: {:?}", data);
}
HostWriteOutcome::Handled
} else if handle == self.input_cccd_handle {
HostWriteOutcome::CccdUpdated
} else if handle == self.control_point_handle {
HostWriteOutcome::ControlPoint
} else {
HostWriteOutcome::Unhandled
}
}
pub(crate) async fn run<'stack, 'server, P: PacketPool>(
server: &'server Server<'_>,
conn: &GattConnection<'stack, 'server, P>,
service: &VialService<'_>,
) {
VIAL_BLE_RX_CHANNEL.clear();
let mut rx = VialBleRx;
let mut tx = VialBleTx {
input_data: server.vial_service.input_data,
conn,
};
service.run_session(&mut rx, &mut tx).await;
}
}
struct VialBleRx;
impl ErrorType for VialBleRx {
type Error = HostTransportError;
}
impl Read for VialBleRx {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let chunk = VIAL_BLE_RX_CHANNEL.receive().await;
if buf.len() < chunk.len() {
error!(
"VialBleRx::read called with buf.len() = {} < chunk.len() = {}",
buf.len(),
chunk.len()
);
return Err(HostTransportError);
}
buf[..chunk.len()].copy_from_slice(&chunk);
Ok(chunk.len())
}
}
struct VialBleTx<'a, 'b, 'c, P: PacketPool> {
input_data: Characteristic<[u8; 32]>,
conn: &'a GattConnection<'b, 'c, P>,
}
impl<P: PacketPool> ErrorType for VialBleTx<'_, '_, '_, P> {
type Error = HostTransportError;
}
impl<P: PacketPool> Write for VialBleTx<'_, '_, '_, P> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let arr: &[u8; 32] = buf.try_into().map_err(|_| {
error!("Vial reply must be exactly 32 bytes, got {}", buf.len());
HostTransportError
})?;
if let Err(e) = self.input_data.notify(self.conn, arr, true).await {
error!("Failed to notify Vial reply: {:?}", e);
return Err(HostTransportError);
}
Ok(buf.len())
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classifies_vial_gatt_handles() {
use crate::test_support::test_block_on as block_on;
let server = Server::new_default("rmk").unwrap();
let mut handler = HostGattHandler::new(&server);
assert_eq!(
block_on(handler.handle_write(server.vial_service.output_data.handle, &[], true)),
HostWriteOutcome::Handled
);
assert_eq!(
block_on(handler.handle_write(server.vial_service.input_data.cccd_handle.unwrap(), &[], true,)),
HostWriteOutcome::CccdUpdated
);
assert_eq!(
block_on(handler.handle_write(server.vial_service.hid_control_point.handle, &[0], true,)),
HostWriteOutcome::ControlPoint
);
assert_eq!(
block_on(handler.handle_write(u16::MAX, &[], true)),
HostWriteOutcome::Unhandled
);
}
}