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

Commit a017629b authored by Qasim Javed's avatar Qasim Javed
Browse files

Kick off Gabeldorsche Rust implementation.

Details at go/rusty-gd

Bug: 171749953
Tag: #gd-refactor
Test: gd/cert/run --host

Unit tests will be added in later CLs

Change-Id: I938579e50fdb8b6f425a12b2d8ccc2971a16bbb4
parent 3f2a3af4
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