use rootservice_grpc::{create_root_facade, RootFacade};
use bt_hal::facade::HciHalFacadeService;
-use bt_hal::hal_facade_module;
+use bt_hal::hal_module;
use bt_hal::rootcanal_hal::RootcanalConfig;
use bt_hci::facade::HciLayerFacadeService;
use bt_hci::hci_module;
module! {
stack_module,
submodules {
- hal_facade_module,
+ hal_module,
hci_module,
}
}
let registry = {
let mut builder = RegistryBuilder::new();
builder.register_module(stack_module);
- if rootcanal_port.is_some() {
- builder.register_module(bt_hal::rootcanal_hal_module);
- }
-
Arc::new(builder.build())
};
"libprotobuf",
"libbt_packets",
"libgddi",
+ "libcxx",
+ "liblazy_static",
],
host_supported: true,
+ target: {
+ android: {
+ static_libs: ["libbt_hidl_hal_cxx"],
+ shared_libs: [
+ "android.hardware.bluetooth@1.0",
+ "libhidlbase",
+ "libutils",
+ ],
+ },
+ },
+}
+
+genrule {
+ name: "libbt_hidl_hal_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header > $(out)",
+ srcs: ["src/hidl_hal.rs"],
+ out: ["src/hidl_hal.rs.h"],
+}
+
+genrule {
+ name: "libbt_hidl_hal_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["src/hidl_hal.rs"],
+ out: ["hidl_hal_generated.cc"],
+}
+
+cc_library_static {
+ name: "libbt_hidl_hal_cxx",
+ srcs: ["src/hidl/interop.cc"],
+ local_include_dirs: ["src/hidl"],
+ generated_headers: ["libbt_hidl_hal_bridge_header", "cxx-bridge-header"],
+ generated_sources: ["libbt_hidl_hal_bridge_code"],
+ shared_libs: [
+ "android.hardware.bluetooth@1.0",
+ "libhidlbase",
+ "libutils",
+ ],
}
--- /dev/null
+#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
+#include <android/hardware/bluetooth/1.0/types.h>
+#include <stdlib.h>
+
+#include "../../os/log.h"
+#include "src/hidl/interop.h"
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks;
+using HidlStatus = ::android::hardware::bluetooth::V1_0::Status;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+class HciDeathRecipient : public ::android::hardware::hidl_death_recipient {
+ public:
+ virtual void serviceDied(uint64_t /*cookie*/, const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+ LOG_ERROR("Bluetooth HAL service died!");
+ abort();
+ }
+};
+
+class HciCallbackTrampoline : public IBluetoothHciCallbacks {
+ public:
+ HciCallbackTrampoline() {}
+
+ Return<void> initializationComplete(HidlStatus status) {
+ ASSERT(status == HidlStatus::SUCCESS);
+ on_init_complete();
+ return Void();
+ }
+
+ Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
+ on_event(rust::Slice(&event[0], event.size()));
+ return Void();
+ }
+
+ Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
+ on_acl(rust::Slice(&data[0], data.size()));
+ return Void();
+ }
+
+ Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
+ on_sco(rust::Slice(&data[0], data.size()));
+ return Void();
+ }
+};
+
+android::sp<HciDeathRecipient> hci_death_recipient_ = new HciDeathRecipient();
+android::sp<IBluetoothHci> bt_hci_;
+android::sp<HciCallbackTrampoline> trampoline_;
+
+} // namespace
+
+void start_hal() {
+ ASSERT(bt_hci_ == nullptr);
+
+ bt_hci_ = IBluetoothHci::getService();
+ ASSERT(bt_hci_ != nullptr);
+ auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
+ ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
+
+ trampoline_ = new HciCallbackTrampoline();
+ bt_hci_->initialize(trampoline_);
+}
+
+void stop_hal() {
+ ASSERT(bt_hci_ != nullptr);
+
+ auto death_unlink = bt_hci_->unlinkToDeath(hci_death_recipient_);
+ if (!death_unlink.isOk()) {
+ LOG_ERROR("Error unlinking death recipient from the Bluetooth HAL");
+ }
+ bt_hci_->close();
+ bt_hci_ = nullptr;
+ trampoline_ = nullptr;
+}
+
+void send_command(rust::Slice<uint8_t> data) {
+ ASSERT(bt_hci_ != nullptr);
+ bt_hci_->sendHciCommand(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
+}
+
+void send_acl(rust::Slice<uint8_t> data) {
+ ASSERT(bt_hci_ != nullptr);
+ bt_hci_->sendAclData(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
+}
+
+void send_sco(rust::Slice<uint8_t> data) {
+ ASSERT(bt_hci_ != nullptr);
+ bt_hci_->sendScoData(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
+}
+
+} // namespace hal
+} // namespace bluetooth
--- /dev/null
+#pragma once
+#include "src/hidl_hal.rs.h"
+
+namespace bluetooth {
+namespace hal {
+
+void start_hal();
+void stop_hal();
+void send_command(rust::Slice<uint8_t> data);
+void send_acl(rust::Slice<uint8_t> data);
+void send_sco(rust::Slice<uint8_t> data);
+
+} // namespace hal
+} // namespace bluetooth
--- /dev/null
+//! Implementation of the HAl that talks to BT controller over Android's HIDL
+use crate::{Hal, HalExports};
+use bt_packet::{HciCommand, HciEvent, RawPacket};
+use bytes::Bytes;
+use gddi::{module, provides};
+use std::sync::Arc;
+use std::sync::Mutex;
+use tokio::runtime::Runtime;
+use tokio::select;
+use tokio::sync::mpsc;
+
+module! {
+ hidl_hal_module,
+ providers {
+ HalExports => provide_hidl_hal,
+ }
+}
+
+#[provides]
+async fn provide_hidl_hal(rt: Arc<Runtime>) -> HalExports {
+ let (hal_exports, hal) = Hal::new();
+ let (init_tx, mut init_rx) = mpsc::unbounded_channel();
+ *CALLBACKS.lock().unwrap() = Some(Callbacks {
+ init_tx,
+ evt_tx: hal.evt_tx,
+ acl_tx: hal.acl_tx,
+ });
+ ffi::start_hal();
+ init_rx.recv().await.unwrap();
+
+ rt.spawn(dispatch_outgoing(hal.cmd_rx, hal.acl_rx));
+
+ hal_exports
+}
+
+#[cxx::bridge(namespace = bluetooth::hal)]
+mod ffi {
+ extern "C" {
+ include!("src/hidl/interop.h");
+ fn start_hal();
+ fn stop_hal();
+ fn send_command(data: &[u8]);
+ fn send_acl(data: &[u8]);
+ fn send_sco(data: &[u8]);
+ }
+
+ extern "Rust" {
+ fn on_init_complete();
+ fn on_event(data: &[u8]);
+ fn on_acl(data: &[u8]);
+ fn on_sco(data: &[u8]);
+ }
+}
+
+struct Callbacks {
+ init_tx: mpsc::UnboundedSender<()>,
+ evt_tx: mpsc::UnboundedSender<HciEvent>,
+ acl_tx: mpsc::UnboundedSender<RawPacket>,
+}
+
+lazy_static! {
+ static ref CALLBACKS: Mutex<Option<Callbacks>> = Mutex::new(None);
+}
+
+fn on_init_complete() {
+ let callbacks = CALLBACKS.lock().unwrap();
+ callbacks.as_ref().unwrap().init_tx.send(()).unwrap();
+}
+
+fn on_event(data: &[u8]) {
+ let callbacks = CALLBACKS.lock().unwrap();
+ callbacks
+ .as_ref()
+ .unwrap()
+ .evt_tx
+ .send(Bytes::copy_from_slice(data))
+ .unwrap();
+}
+
+fn on_acl(data: &[u8]) {
+ let callbacks = CALLBACKS.lock().unwrap();
+ callbacks
+ .as_ref()
+ .unwrap()
+ .acl_tx
+ .send(Bytes::copy_from_slice(data))
+ .unwrap();
+}
+
+fn on_sco(_data: &[u8]) {}
+
+async fn dispatch_outgoing(
+ mut cmd_rx: mpsc::UnboundedReceiver<HciCommand>,
+ mut acl_rx: mpsc::UnboundedReceiver<RawPacket>,
+) {
+ loop {
+ select! {
+ Some(cmd) = cmd_rx.recv() => ffi::send_command(&cmd),
+ Some(acl) = acl_rx.recv() => ffi::send_acl(&acl),
+ else => break,
+ }
+ }
+}
//! HCI Hardware Abstraction Layer
//! Supports sending HCI commands to the HAL and receving
//! HCI events from the HAL
+#[cfg(target_os = "android")]
+#[macro_use]
+extern crate lazy_static;
+
pub mod facade;
pub mod rootcanal_hal;
+#[cfg(not(target_os = "android"))]
+use rootcanal_hal::rootcanal_hal_module;
-pub use facade::hal_facade_module;
-pub use rootcanal_hal::rootcanal_hal_module;
+#[cfg(target_os = "android")]
+mod hidl_hal;
+#[cfg(target_os = "android")]
+use hidl_hal::hidl_hal_module;
use std::sync::Arc;
use thiserror::Error;
use tokio::sync::mpsc;
use tokio::sync::Mutex;
+use gddi::module;
use bt_packet::{HciCommand, HciEvent, RawPacket};
+use facade::hal_facade_module;
+
+#[cfg(target_os = "android")]
+module! {
+ hal_module,
+ submodules {
+ hal_facade_module,
+ hidl_hal_module
+ },
+}
+
+#[cfg(not(target_os = "android"))]
+module! {
+ hal_module,
+ submodules {
+ hal_facade_module,
+ rootcanal_hal_module
+ },
+}
/// H4 packet header size
const H4_HEADER_SIZE: usize = 1;