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

Commit 9b198eaa authored by Zach Johnson's avatar Zach Johnson
Browse files

rusty-gd: start classic acl manager

Bug: 171749953
Tag: #gd-refactor
Test: gd/cert/run --rhost
Change-Id: Ieeaff245a4f19f39311d4ce779576760c6a9f14a
parent f6ebfd4a
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
//! Classic ACL manager

use bt_hci::{Address, CommandSender};
use bt_packets::hci::{
    ClockOffsetValid, CreateConnectionBuilder, CreateConnectionCancelBuilder,
    CreateConnectionRoleSwitch, PageScanRepetitionMode,
};
use gddi::{module, provides, Stoppable};
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio::select;
use tokio::sync::mpsc::{channel, Sender};
use tokio::sync::oneshot;

module! {
    classic_acl_module,
    providers {
        AclManager => provide_acl_manager,
    },
}

/// Classic ACL manager
#[derive(Clone, Stoppable)]
pub struct AclManager {
    req_tx: Sender<Request>,
}

impl AclManager {
    /// Connect to the specified address, or queue it if a connection is already pending
    pub async fn connect(&mut self, addr: Address) {
        self.req_tx.send(Request::Connect { addr }).await.unwrap();
    }

    /// Cancel the connection to the specified address, if it is pending
    pub async fn cancel_connect(&mut self, addr: Address) {
        let (tx, rx) = oneshot::channel();
        self.req_tx.send(Request::CancelConnect { addr, fut: tx }).await.unwrap();
        rx.await.unwrap();
    }
}

#[derive(Debug)]
enum Request {
    Connect { addr: Address },
    CancelConnect { addr: Address, fut: oneshot::Sender<()> },
}

#[provides]
async fn provide_acl_manager(mut hci: CommandSender, rt: Arc<Runtime>) -> AclManager {
    let (req_tx, mut req_rx) = channel::<Request>(10);

    rt.spawn(async move {
        let mut pending_connects: Vec<Address> = Vec::new();
        let mut outgoing_connect: Option<Address> = None;
        loop {
            select! {
                Some(req) = req_rx.recv() => {
                    match req {
                        Request::Connect { addr } => {
                            if outgoing_connect.is_none() {
                                outgoing_connect = Some(addr);
                                hci.send(build_create_connection(addr)).await;
                            } else {
                                pending_connects.insert(0, addr);
                            }
                        },
                        Request::CancelConnect { addr, fut } => {
                            pending_connects.retain(|p| *p != addr);
                            if outgoing_connect == Some(addr) {
                                hci.send(CreateConnectionCancelBuilder { bd_addr: addr }).await;
                            }
                            fut.send(()).unwrap();
                        }
                    }
                }
            }
        }
    });

    AclManager { req_tx }
}

fn build_create_connection(bd_addr: Address) -> CreateConnectionBuilder {
    CreateConnectionBuilder {
        bd_addr,
        packet_type: 0x4408 /* DM 1,3,5 */ | 0x8810, /*DH 1,3,5 */
        page_scan_repetition_mode: PageScanRepetitionMode::R1,
        clock_offset: 0,
        clock_offset_valid: ClockOffsetValid::Invalid,
        allow_role_switch: CreateConnectionRoleSwitch::AllowRoleSwitch,
    }
}
+5 −0
Original line number Diff line number Diff line
//! ACL management

/// Exposes classic ACL functionality
pub mod classic;
mod fragment;

use bt_common::Bluetooth::{self, Classic, Le};
@@ -21,6 +23,9 @@ use tokio::sync::{oneshot, Mutex};

module! {
    acl_module,
    submodules {
        classic::classic_acl_module,
    },
    providers {
        AclDispatch => provide_acl_dispatch,
    },