blob: 8a92ff446846cb6aed3b005914f2bb2ed6b90fd3 [file] [log] [blame]
//! Rootcanal HAL
//! This connects to "rootcanal" which provides a simulated
//! Bluetooth chip as well as a simulated environment.
use crate::hal::internal::{InnerHal, RawHal};
use crate::hal::{Result, H4_HEADER_SIZE};
use bt_packets::hci::{AclPacket, CommandPacket, EventPacket, IsoPacket, Packet};
use bytes::{BufMut, Bytes, BytesMut};
use gddi::{module, provides, Stoppable};
use num_derive::{FromPrimitive, ToPrimitive};
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;
use tokio::runtime::Runtime;
use tokio::select;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
#[derive(FromPrimitive, ToPrimitive)]
enum HciPacketType {
Command = 0x01,
Acl = 0x02,
Sco = 0x03,
Event = 0x04,
Iso = 0x05,
}
const SIZE_OF_EVENT_HEADER: usize = 2;
const _SIZE_OF_SCO_HEADER: usize = 3;
const SIZE_OF_ACL_HEADER: usize = 4;
const SIZE_OF_ISO_HEADER: usize = 4;
module! {
rootcanal_hal_module,
providers {
RawHal => provide_rootcanal_hal,
}
}
#[provides]
async fn provide_rootcanal_hal(config: RootcanalConfig, rt: Arc<Runtime>) -> RawHal {
let (raw_hal, inner_hal) = InnerHal::new();
let (reader, writer) = TcpStream::connect(&config.to_socket_addr().unwrap())
.await
.expect("unable to create stream to rootcanal")
.into_split();
rt.spawn(dispatch_incoming(inner_hal.evt_tx, inner_hal.acl_tx, inner_hal.iso_tx, reader));
rt.spawn(dispatch_outgoing(inner_hal.cmd_rx, inner_hal.acl_rx, inner_hal.iso_rx, writer));
raw_hal
}
/// Rootcanal configuration
#[derive(Clone, Debug, Default, Stoppable)]
pub struct RootcanalConfig {
address: String,
port: u16,
}
impl RootcanalConfig {
/// Create a rootcanal config
pub fn new(address: &str, port: u16) -> Self {
Self { address: String::from(address), port }
}
fn to_socket_addr(&self) -> Result<SocketAddr> {
Ok(SocketAddr::new(IpAddr::from_str(&self.address)?, self.port))
}
}
/// Send HCI events received from the HAL to the HCI layer
async fn dispatch_incoming<R>(
evt_tx: UnboundedSender<EventPacket>,
acl_tx: UnboundedSender<AclPacket>,
iso_tx: UnboundedSender<IsoPacket>,
reader: R,
) -> Result<()>
where
R: AsyncReadExt + Unpin,
{
let mut reader = BufReader::new(reader);
loop {
let mut buffer = BytesMut::with_capacity(1024);
buffer.resize(H4_HEADER_SIZE, 0);
reader.read_exact(&mut buffer).await?;
if buffer[0] == HciPacketType::Event as u8 {
buffer.resize(SIZE_OF_EVENT_HEADER, 0);
reader.read_exact(&mut buffer).await?;
let len: usize = buffer[1].into();
let mut payload = buffer.split_off(SIZE_OF_EVENT_HEADER);
payload.resize(len, 0);
reader.read_exact(&mut payload).await?;
buffer.unsplit(payload);
let frozen = buffer.freeze();
match EventPacket::parse(&frozen) {
Ok(p) => evt_tx.send(p).unwrap(),
Err(e) => log::error!("dropping invalid event packet: {}: {:02x}", e, frozen),
}
} else if buffer[0] == HciPacketType::Acl as u8 {
buffer.resize(SIZE_OF_ACL_HEADER, 0);
reader.read_exact(&mut buffer).await?;
let len: usize = (buffer[2] as u16 + ((buffer[3] as u16) << 8)).into();
let mut payload = buffer.split_off(SIZE_OF_ACL_HEADER);
payload.resize(len, 0);
reader.read_exact(&mut payload).await?;
buffer.unsplit(payload);
let frozen = buffer.freeze();
match AclPacket::parse(&frozen) {
Ok(p) => acl_tx.send(p).unwrap(),
Err(e) => log::error!("dropping invalid ACL packet: {}: {:02x}", e, frozen),
}
} else if buffer[0] == HciPacketType::Iso as u8 {
buffer.resize(SIZE_OF_ISO_HEADER, 0);
reader.read_exact(&mut buffer).await?;
let len: usize = (buffer[2] as u16 + (((buffer[3] & 0x3f) as u16) << 8)).into();
let mut payload = buffer.split_off(SIZE_OF_ISO_HEADER);
payload.resize(len, 0);
reader.read_exact(&mut payload).await?;
buffer.unsplit(payload);
let frozen = buffer.freeze();
match IsoPacket::parse(&frozen) {
Ok(p) => iso_tx.send(p).unwrap(),
Err(e) => log::error!("dropping invalid ISO packet: {}: {:02x}", e, frozen),
}
}
}
}
/// Send commands received from the HCI later to rootcanal
async fn dispatch_outgoing<W>(
mut cmd_rx: UnboundedReceiver<CommandPacket>,
mut acl_rx: UnboundedReceiver<AclPacket>,
mut iso_rx: UnboundedReceiver<IsoPacket>,
mut writer: W,
) -> Result<()>
where
W: AsyncWriteExt + Unpin,
{
loop {
select! {
Some(cmd) = cmd_rx.recv() => write_with_type(&mut writer, HciPacketType::Command, cmd.to_bytes()).await?,
Some(acl) = acl_rx.recv() => write_with_type(&mut writer, HciPacketType::Acl, acl.to_bytes()).await?,
Some(iso) = iso_rx.recv() => write_with_type(&mut writer, HciPacketType::Iso, iso.to_bytes()).await?,
else => break,
}
}
Ok(())
}
async fn write_with_type<W>(writer: &mut W, t: HciPacketType, b: Bytes) -> Result<()>
where
W: AsyncWriteExt + Unpin,
{
let mut data = BytesMut::with_capacity(b.len() + 1);
data.put_u8(t as u8);
data.extend(b);
writer.write_all(&data[..]).await?;
Ok(())
}