Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d874bd6 authored by Zach Johnson's avatar Zach Johnson Committed by Gerrit Code Review
Browse files

Merge "Kick off Gabeldorsche Rust implementation."

parents 569d1ba7 a017629b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -762,3 +762,12 @@ cc_library_host_shared {
    ],
    rtti: true,
}

rust_protobuf {
    name: "libhci_layer_facade_proto",
    crate_name: "hci_layer_facade_proto",
    proto: "hci/facade/facade.proto",
    proto_flags: ["-Iexternal/protobuf/src/"],
    source_stem: "facade",
    host_supported: true,
}
+14 −0
Original line number Diff line number Diff line
rust_library {
    name: "libbt_hal",
    crate_name: "bt_hal",
    srcs: ["src/lib.rs"],
    edition: "2018",
    rustlibs: [
        "libbt_packet",
        "libbytes",
        "libfutures",
        "libthiserror",
        "libtokio",
    ],
    host_supported: true,
}
+54 −0
Original line number Diff line number Diff line
//! HCI Hardware Abstraction Layer
//! Supports sending HCI commands to the HAL and receving
//! HCI events from the HAL
pub mod rootcanal_hal;

use thiserror::Error;
use tokio::sync::mpsc;

use bt_packet::{HciCommand, HciEvent};

/// H4 packet header size
const H4_HEADER_SIZE: usize = 1;

/// HAL interface
/// This is used by the HCI module to send commands to the
/// HAL and receive events from the HAL
pub struct HalExports {
    /// Transmit end of a channel used to send HCI commands
    pub cmd_tx: mpsc::UnboundedSender<HciCommand>,
    /// Receive end of a channel used to receive HCI events
    pub evt_rx: mpsc::UnboundedReceiver<HciEvent>,
}

/// HCI HAL
/// Receive HCI commands, send HCI events
pub struct Hal {
    /// Receive end of a channel used to receive HCI commands
    pub cmd_rx: mpsc::UnboundedReceiver<HciCommand>,
    /// Transmit end of a channel used to send HCI events
    pub evt_tx: mpsc::UnboundedSender<HciEvent>,
}

impl Hal {
    /// Create a new Hal instance
    pub fn new() -> (HalExports, Self) {
        let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
        let (evt_tx, evt_rx) = mpsc::unbounded_channel();
        (HalExports { cmd_tx, evt_rx }, Self { cmd_rx, evt_tx })
    }
}

/// Result type
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

/// Errors that can be encountered while dealing with the HAL
#[derive(Error, Debug)]
pub enum HalError {
    /// Invalid rootcanal host error
    #[error("Invalid rootcanal host")]
    InvalidAddressError,
    /// Error while connecting to rootcanal
    #[error("Connection to rootcanal failed: {0}")]
    RootcanalConnectError(#[from] tokio::io::Error),
}
+94 −0
Original line number Diff line number Diff line
//! Rootcanal HAL
//! This connects to "rootcanal" which provides a simulated
//! Bluetooth chip as well as a simulated environment.

use bytes::{BufMut, BytesMut};
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;

use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;

use tokio::runtime::Runtime;

use futures::stream::StreamExt;
use tokio::sync::mpsc;

use bt_packet::{HciCommand, HciEvent, HciPacketHeaderSize, HciPacketType};

use std::sync::Arc;

use crate::{Hal, HalExports, Result, H4_HEADER_SIZE};

/// Rootcanal configuration
#[derive(Clone, Debug, Default)]
pub struct RootcanalConfig {
    port: u16,
    server_address: String,
}

impl RootcanalConfig {
    /// Create a rootcanal config
    pub fn new(port: u16, server_address: &str) -> Self {
        Self { port, server_address: String::from(server_address) }
    }
}

/// Rootcanal HAL
#[derive(Default)]
pub struct RootcanalHal;

impl RootcanalHal {
    /// Send HCI events received from the HAL to the HCI layer
    async fn dispatch_incoming<R>(evt_tx: mpsc::UnboundedSender<HciEvent>, reader: R) -> Result<()>
    where
        R: AsyncReadExt + Unpin,
    {
        let mut reader = BufReader::new(reader);
        let header_size = H4_HEADER_SIZE + HciPacketHeaderSize::Event as usize;
        loop {
            let mut header = BytesMut::with_capacity(1024);
            header.resize(header_size, 0);
            reader.read_exact(&mut header).await?;
            let param_len: usize = header[2].into();
            let mut payload = header.split_off(header_size);
            payload.resize(param_len, 0);
            reader.read_exact(&mut payload).await?;
            let h4_type = header.split_to(H4_HEADER_SIZE);
            header.unsplit(payload);
            if h4_type[0] == HciPacketType::Event as u8 {
                evt_tx.send(header.freeze()).unwrap();
            }
        }
    }

    /// Send commands received from the HCI later to rootcanal
    async fn dispatch_outgoing<W>(
        mut cmd_rx: mpsc::UnboundedReceiver<HciCommand>,
        mut writer: W,
    ) -> Result<()>
    where
        W: AsyncWriteExt + Unpin,
    {
        while let Some(next_cmd) = cmd_rx.next().await {
            let mut command = BytesMut::with_capacity(next_cmd.len() + 1);
            command.put_u8(HciPacketType::Command as u8);
            command.extend(next_cmd);
            writer.write_all(&command[..]).await?;
        }
        Ok(())
    }

    /// Connect to rootcanal and spawn tasks that handle incoming and outgoing packets
    pub async fn start(config: RootcanalConfig, rt: Arc<Runtime>) -> Result<HalExports> {
        let (hal_exports, hal) = Hal::new();
        let ipaddr = IpAddr::from_str(&config.server_address)?;
        let socket_addr = SocketAddr::new(ipaddr, config.port);
        let stream = TcpStream::connect(&socket_addr).await?;
        let (reader, writer) = stream.into_split();

        rt.spawn(Self::dispatch_incoming(hal.evt_tx, reader));
        rt.spawn(Self::dispatch_outgoing(hal.cmd_rx, writer));
        Ok(hal_exports)
    }
}
+56 −0
Original line number Diff line number Diff line
rust_library {
    name: "libbt_hci",
    crate_name: "bt_hci",
    srcs: ["src/lib.rs"],
    edition: "2018",
    rustlibs: [
        "libbt_hal",
        "libbt_packet",
        "libbytes",
        "libfutures",
        "libgrpcio",
        "libhci_layer_facade_proto",
        "libnum_traits",
        "libthiserror",
        "libtokio",
        "libprotobuf",
    ],
    proc_macros: [
        "libnum_derive",
    ],
    host_supported: true,
}

rust_binary {
    name: "hci_facade_client",
    crate_name: "hci_facade_client",
    srcs: ["src/facade/hci_facade_client.rs"],
    edition: "2018",
    rustlibs: [
        "libbt_hal",
        "libbt_hci",
        "libbytes",
        "libfutures",
        "libgrpcio",
        "libprotobuf",
        "libtokio",
    ],
    host_supported: true,
}

rust_binary {
    name: "hci_facade_main",
    crate_name: "hci_facade_main",
    srcs: ["src/facade/hci_facade_main.rs"],
    edition: "2018",
    rustlibs: [
        "libbt_hal",
        "libbt_hci",
        "libbytes",
        "libfutures",
        "libgrpcio",
        "libprotobuf",
        "libtokio",
    ],
    host_supported: true,
}
Loading