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

Commit 5e50bfda authored by Sonny Sasaka's avatar Sonny Sasaka
Browse files

floss: Add GATT topshim

This adds topshim layer for floss Rust to FFI with bt_gatt.h. In this
patch only btgatt_client_interface_t and btgatt_server_interface_t are
shimmed, and the remaining BleScannerInterface and
BleAdvertiserInterface will be added separately using cxx.

Bug: 193916778
Tag: #floss
Test: Build floss on Linux

Change-Id: Id2857d7674222e1dccc906bf002d479c3af6c39f
parent 32cd6595
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -101,9 +101,11 @@ rust_bindgen {
        "--size_t-is-usize",
        "--allowlist-function=bt_.*",
        "--allowlist-function=bthh_.*",
        "--allowlist-function=btgatt_.*",
        "--allowlist-function=hal_util_.*",
        "--allowlist-type=bt_.*",
        "--allowlist-type=bthh_.*",
        "--allowlist-type=btgatt_.*",
        "--enable-cxx-namespaces",
        "--opaque-type=std::.*",
        "--with-derive-default",
+1 −0
Original line number Diff line number Diff line
@@ -7,4 +7,5 @@
// Profiles

// Hid host profile
#include "hardware/bt_gatt.h"
#include "hardware/bt_hh.h"
+3 −2
Original line number Diff line number Diff line
@@ -34,8 +34,9 @@ fn main() {
        .clang_args(libchrome_paths)
        .clang_args(clang_args)
        .enable_cxx_namespaces()
        .whitelist_type("(bt_|bthh_).*")
        .whitelist_function("(bt_|bthh_).*")
        .size_t_is_usize(true)
        .whitelist_type("(bt_|bthh_|btgatt_).*")
        .whitelist_function("(bt_|bthh_|btgatt_).*")
        .whitelist_function("hal_util_.*")
        // We must opaque out std:: in order to prevent bindgen from choking
        .opaque_type("std::.*")
+10 −3
Original line number Diff line number Diff line
@@ -239,17 +239,24 @@ pub type BtHciErrorCode = u8;

pub type BtPinCode = bindings::bt_pin_code_t;

pub type Uuid = bindings::bluetooth::Uuid;

pub enum SupportedProfiles {
    HidHost,
    A2dp,
    Gatt,
}

impl From<SupportedProfiles> for Vec<u8> {
    fn from(item: SupportedProfiles) -> Self {
        match item {
            SupportedProfiles::HidHost => "hidhost".bytes().collect::<Vec<u8>>(),
            SupportedProfiles::A2dp => "a2dp".bytes().collect::<Vec<u8>>(),
            SupportedProfiles::HidHost => "hidhost",
            SupportedProfiles::A2dp => "a2dp",
            SupportedProfiles::Gatt => "gatt",
        }
        .bytes()
        .chain("\0".bytes())
        .collect::<Vec<u8>>()
    }
}

@@ -268,7 +275,7 @@ mod ffi {
}

// Export the raw address type directly from the bindings
type FfiAddress = bindings::RawAddress;
pub type FfiAddress = bindings::RawAddress;

/// A shared address structure that has the same representation as
/// bindings::RawAddress. Macros `deref_ffi_address` and `cast_to_ffi_address`
+512 −0
Original line number Diff line number Diff line
use crate::bindings::root as bindings;
use crate::btif::{BluetoothInterface, BtStatus, FfiAddress, RawAddress, SupportedProfiles, Uuid};
use crate::profiles::gatt::bindings::{
    btgatt_callbacks_t, btgatt_client_callbacks_t, btgatt_client_interface_t, btgatt_interface_t,
    btgatt_scanner_callbacks_t, btgatt_server_callbacks_t, btgatt_server_interface_t,
    BleAdvertiserInterface, BleScannerInterface,
};
use crate::topstack::get_dispatchers;
use crate::{cast_to_ffi_address, ccall};

use std::sync::{Arc, Mutex};

use topshim_macros::cb_variant;

pub type BtGattNotifyParams = bindings::btgatt_notify_params_t;
pub type BtGattReadParams = bindings::btgatt_read_params_t;
pub type BtGattDbElement = bindings::btgatt_db_element_t;
pub type BtGattResponse = bindings::btgatt_response_t;
pub type BtGattTestParams = bindings::btgatt_test_params_t;

#[derive(Debug)]
pub enum GattClientCallbacks {
    RegisterClient(i32, i32, *const Uuid),
}

pub struct GattClientCallbacksDispatcher {
    pub dispatch: Box<dyn Fn(GattClientCallbacks) + Send>,
}

type GattClientCb = Arc<Mutex<GattClientCallbacksDispatcher>>;

cb_variant!(
    GattClientCb,
    gc_register_client_cb -> GattClientCallbacks::RegisterClient,
    i32, i32, *const Uuid, {}
);

struct RawGattWrapper {
    raw: *const btgatt_interface_t,
}

struct RawGattClientWrapper {
    raw: *const btgatt_client_interface_t,
}

struct RawGattServerWrapper {
    raw: *const btgatt_server_interface_t,
}

struct RawBleScannerWrapper {
    _raw: *const BleScannerInterface,
}

struct RawBleAdvertiserWrapper {
    _raw: *const BleAdvertiserInterface,
}

// Pointers unsafe due to ownership but this is a static pointer so Send is ok
unsafe impl Send for RawGattWrapper {}
unsafe impl Send for RawGattClientWrapper {}
unsafe impl Send for RawGattServerWrapper {}
unsafe impl Send for RawBleScannerWrapper {}
unsafe impl Send for RawBleAdvertiserWrapper {}
unsafe impl Send for btgatt_callbacks_t {}

pub struct GattClient {
    internal: RawGattClientWrapper,
}

impl GattClient {
    pub fn register_client(&self, uuid: &Uuid, eatt_support: bool) -> BtStatus {
        BtStatus::from(ccall!(self, register_client, uuid, eatt_support))
    }

    pub fn unregister_client(&self, client_if: i32) -> BtStatus {
        BtStatus::from(ccall!(self, unregister_client, client_if))
    }

    pub fn connect(
        &self,
        client_if: i32,
        addr: &mut RawAddress,
        is_direct: bool,
        transport: i32,
        opportunistic: bool,
        initiating_phys: i32,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(
            self,
            connect,
            client_if,
            ffi_addr,
            is_direct,
            transport,
            opportunistic,
            initiating_phys
        ))
    }

    pub fn disconnect(&self, client_if: i32, addr: &mut RawAddress, conn_id: i32) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, disconnect, client_if, ffi_addr, conn_id))
    }

    pub fn refresh(&self, client_if: i32, addr: &mut RawAddress) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, refresh, client_if, ffi_addr))
    }

    pub fn search_service(&self, conn_id: i32, filter_uuid: &Uuid) -> BtStatus {
        BtStatus::from(ccall!(self, search_service, conn_id, filter_uuid))
    }

    pub fn btif_gattc_discover_service_by_uuid(&self, conn_id: i32, uuid: &Uuid) {
        ccall!(self, btif_gattc_discover_service_by_uuid, conn_id, uuid)
    }

    pub fn read_characteristic(&self, conn_id: i32, handle: u16, auth_req: i32) -> BtStatus {
        BtStatus::from(ccall!(self, read_characteristic, conn_id, handle, auth_req))
    }

    pub fn read_using_characteristic_uuid(
        &self,
        conn_id: i32,
        uuid: &Uuid,
        s_handle: u16,
        e_handle: u16,
        auth_req: i32,
    ) -> BtStatus {
        BtStatus::from(ccall!(
            self,
            read_using_characteristic_uuid,
            conn_id,
            uuid,
            s_handle,
            e_handle,
            auth_req
        ))
    }

    pub fn write_characteristic(
        &self,
        conn_id: i32,
        handle: u16,
        write_type: i32,
        auth_req: i32,
        value: &[u8],
    ) -> BtStatus {
        BtStatus::from(ccall!(
            self,
            write_characteristic,
            conn_id,
            handle,
            write_type,
            auth_req,
            value.as_ptr(),
            value.len()
        ))
    }

    pub fn read_descriptor(&self, conn_id: i32, handle: u16, auth_req: i32) -> BtStatus {
        BtStatus::from(ccall!(self, read_descriptor, conn_id, handle, auth_req))
    }

    pub fn write_descriptor(
        &self,
        conn_id: i32,
        handle: u16,
        auth_req: i32,
        value: &[u8],
    ) -> BtStatus {
        BtStatus::from(ccall!(
            self,
            write_descriptor,
            conn_id,
            handle,
            auth_req,
            value.as_ptr(),
            value.len()
        ))
    }

    pub fn execute_write(&self, conn_id: i32, execute: i32) -> BtStatus {
        BtStatus::from(ccall!(self, execute_write, conn_id, execute))
    }

    pub fn register_for_notification(
        &self,
        client_if: i32,
        addr: &mut RawAddress,
        handle: u16,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, register_for_notification, client_if, ffi_addr, handle))
    }

    pub fn deregister_for_notification(
        &self,
        client_if: i32,
        addr: &mut RawAddress,
        handle: u16,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, deregister_for_notification, client_if, ffi_addr, handle))
    }

    pub fn read_remote_rssi(&self, client_if: i32, addr: &mut RawAddress) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, read_remote_rssi, client_if, ffi_addr))
    }

    pub fn get_device_type(&self, addr: &mut RawAddress) -> i32 {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        ccall!(self, get_device_type, ffi_addr)
    }

    pub fn configure_mtu(&self, conn_id: i32, mtu: i32) -> BtStatus {
        BtStatus::from(ccall!(self, configure_mtu, conn_id, mtu))
    }

    pub fn conn_parameter_update(
        &self,
        addr: &mut RawAddress,
        min_interval: i32,
        max_interval: i32,
        latency: i32,
        timeout: i32,
        min_ce_len: u16,
        max_ce_len: u16,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(
            self,
            conn_parameter_update,
            ffi_addr,
            min_interval,
            max_interval,
            latency,
            timeout,
            min_ce_len,
            max_ce_len
        ))
    }

    pub fn set_preferred_phy(
        &self,
        addr: &mut RawAddress,
        tx_phy: u8,
        rx_phy: u8,
        phy_options: u16,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, set_preferred_phy, ffi_addr, tx_phy, rx_phy, phy_options))
    }

    // TODO(b/193916778): Figure out how to shim read_phy which accepts base::Callback

    pub fn test_command(&self, command: i32, params: &BtGattTestParams) -> BtStatus {
        BtStatus::from(ccall!(self, test_command, command, params))
    }

    pub fn get_gatt_db(&self, conn_id: i32) -> BtStatus {
        BtStatus::from(ccall!(self, get_gatt_db, conn_id))
    }
}

pub struct GattServer {
    internal: RawGattServerWrapper,
}

impl GattServer {
    pub fn register_server(&self, uuid: &Uuid, eatt_support: bool) -> BtStatus {
        BtStatus::from(ccall!(self, register_server, uuid, eatt_support))
    }

    pub fn unregister_server(&self, server_if: i32) -> BtStatus {
        BtStatus::from(ccall!(self, unregister_server, server_if))
    }

    pub fn connect(
        &self,
        server_if: i32,
        addr: &mut RawAddress,
        is_direct: bool,
        transport: i32,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, connect, server_if, ffi_addr, is_direct, transport))
    }

    pub fn disconnect(&self, server_if: i32, addr: &mut RawAddress, conn_id: i32) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, disconnect, server_if, ffi_addr, conn_id))
    }

    pub fn add_service(&self, server_if: i32, service: &[BtGattDbElement]) -> BtStatus {
        BtStatus::from(ccall!(self, add_service, server_if, service.as_ptr(), service.len()))
    }

    pub fn stop_service(&self, server_if: i32, service_handle: i32) -> BtStatus {
        BtStatus::from(ccall!(self, stop_service, server_if, service_handle))
    }

    pub fn delete_service(&self, server_if: i32, service_handle: i32) -> BtStatus {
        BtStatus::from(ccall!(self, delete_service, server_if, service_handle))
    }

    pub fn send_indication(
        &self,
        server_if: i32,
        attribute_handle: i32,
        conn_id: i32,
        confirm: i32,
        value: &[u8],
    ) -> BtStatus {
        BtStatus::from(ccall!(
            self,
            send_indication,
            server_if,
            attribute_handle,
            conn_id,
            confirm,
            value.as_ptr(),
            value.len()
        ))
    }

    pub fn send_response(
        &self,
        conn_id: i32,
        trans_id: i32,
        status: i32,
        response: &BtGattResponse,
    ) -> BtStatus {
        BtStatus::from(ccall!(self, send_response, conn_id, trans_id, status, response))
    }

    pub fn set_preferred_phy(
        &self,
        addr: &mut RawAddress,
        tx_phy: u8,
        rx_phy: u8,
        phy_options: u16,
    ) -> BtStatus {
        let ffi_addr = cast_to_ffi_address!(addr as *mut RawAddress);
        BtStatus::from(ccall!(self, set_preferred_phy, ffi_addr, tx_phy, rx_phy, phy_options))
    }

    // TODO(b/193916778): Figure out how to shim read_phy which accepts base::Callback
}

// TODO(b/193916778): Underlying FFI is C++, implement using cxx.
pub struct BleScanner {
    _internal: RawBleScannerWrapper,
}

// TODO(b/193916778): Underlying FFI is C++, implement using cxx.
pub struct BleAdvertiser {
    _internal: RawBleAdvertiserWrapper,
}

pub struct Gatt {
    internal: RawGattWrapper,
    is_init: bool,

    pub client: GattClient,
    pub server: GattServer,
    pub scanner: BleScanner,
    pub advertiser: BleAdvertiser,

    // Keep callback object in memory (underlying code doesn't make copy)
    callbacks: Option<Box<bindings::btgatt_callbacks_t>>,
    gatt_client_callbacks: Option<Box<bindings::btgatt_client_callbacks_t>>,
    gatt_server_callbacks: Option<Box<bindings::btgatt_server_callbacks_t>>,
    gatt_scanner_callbacks: Option<Box<bindings::btgatt_scanner_callbacks_t>>,
}

impl Gatt {
    pub fn new(intf: &BluetoothInterface) -> Option<Gatt> {
        let r = intf.get_profile_interface(SupportedProfiles::Gatt);

        if r == std::ptr::null() {
            return None;
        }

        Some(Gatt {
            internal: RawGattWrapper { raw: r as *const btgatt_interface_t },
            is_init: false,
            client: GattClient {
                internal: RawGattClientWrapper {
                    raw: unsafe {
                        (*(r as *const btgatt_interface_t)).client
                            as *const btgatt_client_interface_t
                    },
                },
            },
            server: GattServer {
                internal: RawGattServerWrapper {
                    raw: unsafe {
                        (*(r as *const btgatt_interface_t)).server
                            as *const btgatt_server_interface_t
                    },
                },
            },
            scanner: BleScanner {
                _internal: RawBleScannerWrapper {
                    _raw: unsafe {
                        (*(r as *const btgatt_interface_t)).scanner as *const BleScannerInterface
                    },
                },
            },
            advertiser: BleAdvertiser {
                _internal: RawBleAdvertiserWrapper {
                    _raw: unsafe {
                        (*(r as *const btgatt_interface_t)).scanner as *const BleAdvertiserInterface
                    },
                },
            },
            callbacks: None,
            gatt_client_callbacks: None,
            gatt_server_callbacks: None,
            gatt_scanner_callbacks: None,
        })
    }

    pub fn is_initialized(&self) -> bool {
        self.is_init
    }

    pub fn initialize(
        &mut self,
        gatt_client_callbacks_dispatcher: GattClientCallbacksDispatcher,
    ) -> bool {
        // Register dispatcher
        if get_dispatchers()
            .lock()
            .unwrap()
            .set::<GattClientCb>(Arc::new(Mutex::new(gatt_client_callbacks_dispatcher)))
        {
            panic!("Tried to set dispatcher for GattClientCallbacks but it already existed");
        }

        // TODO(b/193916778): Populate all the callbacks
        let mut gatt_client_callbacks = Box::new(btgatt_client_callbacks_t {
            register_client_cb: Some(gc_register_client_cb),
            open_cb: None,
            close_cb: None,
            search_complete_cb: None,
            register_for_notification_cb: None,
            notify_cb: None,
            read_characteristic_cb: None,
            write_characteristic_cb: None,
            read_descriptor_cb: None,
            write_descriptor_cb: None,
            execute_write_cb: None,
            read_remote_rssi_cb: None,
            configure_mtu_cb: None,
            congestion_cb: None,
            get_gatt_db_cb: None,
            services_removed_cb: None,
            services_added_cb: None,
            phy_updated_cb: None,
            conn_updated_cb: None,
            service_changed_cb: None,
        });

        let mut gatt_server_callbacks = Box::new(btgatt_server_callbacks_t {
            register_server_cb: None,
            connection_cb: None,
            service_added_cb: None,
            service_stopped_cb: None,
            service_deleted_cb: None,
            request_read_characteristic_cb: None,
            request_read_descriptor_cb: None,
            request_write_characteristic_cb: None,
            request_write_descriptor_cb: None,
            request_exec_write_cb: None,
            response_confirmation_cb: None,
            indication_sent_cb: None,
            congestion_cb: None,
            mtu_changed_cb: None,
            phy_updated_cb: None,
            conn_updated_cb: None,
        });

        let mut gatt_scanner_callbacks = Box::new(btgatt_scanner_callbacks_t {
            scan_result_cb: None,
            batchscan_reports_cb: None,
            batchscan_threshold_cb: None,
            track_adv_event_cb: None,
        });

        let mut callbacks = Box::new(btgatt_callbacks_t {
            size: 4 * 8,
            client: &mut *gatt_client_callbacks,
            server: &mut *gatt_server_callbacks,
            scanner: &mut *gatt_scanner_callbacks,
        });

        let rawcb = &mut *callbacks;

        let init = ccall!(self, init, rawcb);
        self.is_init = init == 0;
        self.callbacks = Some(callbacks);
        self.gatt_client_callbacks = Some(gatt_client_callbacks);
        self.gatt_server_callbacks = Some(gatt_server_callbacks);
        self.gatt_scanner_callbacks = Some(gatt_scanner_callbacks);

        return self.is_init;
    }
}
Loading