blob: c9c2accfe1989b9190f500f9ebe5a42fa1eebf09 [file] [log] [blame] [edit]
// Copyright 2024, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{HciHal, HciHalStatus, HciProxyCallbacks};
use core::ffi::{c_int, c_void, CStr};
use core::slice;
use std::fs::File;
use std::io::Write;
use std::os::fd::AsRawFd;
use std::sync::{Mutex, RwLock};
/// Callbacks from C to Rust
/// `handle` is allocated as an `Option<T: Callbacks>`; It must be valid from the
/// `CInterface.initialize()` call to the `CInterface.close()` call. This value
/// is returned as the first parameter to all other functions.
/// To prevent scheduling issues from the HAL Implementer, we enforce the validity
/// until the end of `Ffi<T>` instance; aka until the end of process life.
#[repr(C)]
#[allow(dead_code)]
pub struct CCallbacks {
handle: *const c_void,
initialization_complete: unsafe extern "C" fn(*mut c_void, CStatus),
event_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
acl_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
sco_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
iso_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
}
/// C Interface called from Rust
/// `handle` is a pointer initialized by the C code and passed to all other functions.
/// `callbacks` is only valid during the `initialize()` call.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CInterface {
handle: *mut c_void,
initialize: unsafe extern "C" fn(handle: *mut c_void, callbacks: *const CCallbacks),
close: unsafe extern "C" fn(handle: *mut c_void),
send_command: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
send_acl: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
send_sco: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
send_iso: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
client_died: Option<unsafe extern "C" fn(handle: *mut c_void)>,
dump: Option<unsafe extern "C" fn(handle: *mut c_void, fd: c_int)>,
}
//SAFETY: CInterface is safe to send between threads because we require the C code
// which initialises it to only use pointers to functions which are safe
// to call from any thread.
unsafe impl Send for CInterface {}
#[repr(C)]
#[allow(dead_code)]
#[derive(Debug, PartialEq)]
pub(crate) enum CStatus {
Success,
AlreadyInitialized,
UnableToOpenInterface,
HardwareInitializationError,
Unknown,
}
pub(crate) struct Ffi {
intf: Mutex<CInterface>,
wrapper: RwLock<Option<HciProxyCallbacks>>,
}
impl Ffi {
pub(crate) fn new(intf: CInterface) -> Self {
Self { intf: Mutex::new(intf), wrapper: RwLock::new(None) }
}
fn set_client(&self, client: HciProxyCallbacks) {
*self.wrapper.write().unwrap() = Some(client);
}
fn remove_client(&self) {
*self.wrapper.write().unwrap() = None;
}
}
impl HciHal for Ffi {
fn initialize(&self, client: HciProxyCallbacks) {
let intf = self.intf.lock().unwrap();
self.set_client(client);
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer.
unsafe {
(intf.initialize)(intf.handle, &CCallbacks::new(&self.wrapper));
}
}
fn send_command(&self, data: &[u8]) {
let intf = self.intf.lock().unwrap();
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer and an initialized `handle`.
unsafe {
(intf.send_command)(intf.handle, data.as_ptr(), data.len());
}
}
fn send_acl(&self, data: &[u8]) {
let intf = self.intf.lock().unwrap();
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer and an initialized `handle`.
unsafe {
(intf.send_acl)(intf.handle, data.as_ptr(), data.len());
}
}
fn send_iso(&self, data: &[u8]) {
let intf = self.intf.lock().unwrap();
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer and an initialized `handle`.
unsafe {
(intf.send_iso)(intf.handle, data.as_ptr(), data.len());
}
}
fn send_sco(&self, data: &[u8]) {
let intf = self.intf.lock().unwrap();
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer and an initialized `handle`.
unsafe {
(intf.send_sco)(intf.handle, data.as_ptr(), data.len());
}
}
fn close(&self) {
let intf = self.intf.lock().unwrap();
// SAFETY: The C Code has initialized the `CInterface` with a valid
// function pointer and an initialized `handle`.
unsafe {
(intf.close)(intf.handle);
}
self.remove_client();
}
fn client_died(&self) {
let intf = self.intf.lock().unwrap();
if let Some(client_died) = intf.client_died {
// SAFETY: The C Code has initialized the `CInterface` with a valid
// or null function pointer and an initialized `handle`.
unsafe {
client_died(intf.handle);
}
}
self.remove_client();
}
/// # Safety
///
/// The `writer` must be a concrete `File` type, as it will be casted to a
/// `File` pointer to extract the raw file descriptor.
unsafe fn dump(&self, writer: &mut dyn Write, _args: &[&CStr]) {
let intf = self.intf.lock().unwrap();
// SAFETY: The `writer` is guaranteed to be supported by a concrete `File` type,
// by the safety restriction of the function signature.
let fd = unsafe { &*(writer as *mut _ as *mut File) }.as_raw_fd();
if let Some(dump) = intf.dump {
// SAFETY: The C code has initialized the `CInterface` with a valid
// or null function pointer and an initialized `handle`.
unsafe {
dump(intf.handle, fd);
}
}
}
}
impl CCallbacks {
fn new(wrapper: &RwLock<Option<HciProxyCallbacks>>) -> Self {
Self {
handle: (wrapper as *const RwLock<Option<HciProxyCallbacks>>).cast(),
initialization_complete: Self::initialization_complete,
event_received: Self::event_received,
acl_received: Self::acl_received,
sco_received: Self::sco_received,
iso_received: Self::iso_received,
}
}
/// #Safety
///
/// `handle` must be a valid pointer previously passed to the corresponding `initialize()`,
/// and not yet destroyed (this is in fact an `RwLock<Option<T>>`).
unsafe fn unwrap_client<F: FnOnce(&HciProxyCallbacks)>(handle: *mut c_void, f: F) {
let wrapper: *const RwLock<Option<HciProxyCallbacks>> = handle.cast();
// SAFETY: The `handle` points the `RwLock<Option<T>>` wrapper object; it was allocated
// at the creation of the `Ffi` object and remain alive until its destruction.
if let Some(client) = unsafe { &*(*wrapper).read().unwrap() } {
f(client);
} else {
log::error!("FFI Callback called in bad state");
}
}
/// #Safety
///
/// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`
unsafe extern "C" fn initialization_complete(handle: *mut c_void, status: CStatus) {
// SAFETY: The vendor HAL returns `handle` pointing `wrapper` object which has
// the same lifetime as the base `Ffi` instance.
unsafe {
Self::unwrap_client(handle, |client: &HciProxyCallbacks| {
client.initialization_complete(status.into())
});
}
}
/// #Safety
///
/// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
/// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
/// is not mutated for the duration of this call.
unsafe extern "C" fn event_received(handle: *mut c_void, data: *const u8, len: usize) {
// SAFETY: The C code returns `handle` pointing `wrapper` object which has
// the same lifetime as the base `Ffi` instance. `data` points to a buffer
// of `len` bytes valid until the function returns.
unsafe {
Self::unwrap_client(handle, |client: &HciProxyCallbacks| {
client.event_received(slice::from_raw_parts(data, len))
});
}
}
/// #Safety
///
/// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
/// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
/// is not mutated for the duration of this call.
unsafe extern "C" fn acl_received(handle: *mut c_void, data: *const u8, len: usize) {
// SAFETY: The C code returns `handle` pointing `wrapper` object which has
// the same lifetime as the base `Ffi` instance. `data` points to a buffer
// of `len` bytes valid until the function returns.
unsafe {
Self::unwrap_client(handle, |client: &HciProxyCallbacks| {
client.acl_received(slice::from_raw_parts(data, len))
});
}
}
/// #Safety
///
/// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
/// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
/// is not mutated for the duration of this call.
unsafe extern "C" fn sco_received(handle: *mut c_void, data: *const u8, len: usize) {
// SAFETY: The C code returns `handle` pointing `wrapper` object which has
// the same lifetime as the base `Ffi` instance. `data` points to a buffer
// of `len` bytes valid until the function returns.
unsafe {
Self::unwrap_client(handle, |client: &HciProxyCallbacks| {
client.sco_received(slice::from_raw_parts(data, len))
});
}
}
/// #Safety
///
/// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
/// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
/// is not mutated for the duration of this call.
unsafe extern "C" fn iso_received(handle: *mut c_void, data: *const u8, len: usize) {
// SAFETY: The C code returns `handle` pointing `wrapper` object which has
// the same lifetime as the base `Ffi` instance. `data` points to a buffer
// of `len` bytes valid until the function returns.
unsafe {
Self::unwrap_client(handle, |client: &HciProxyCallbacks| {
client.iso_received(slice::from_raw_parts(data, len))
});
}
}
}
impl From<CStatus> for HciHalStatus {
fn from(value: CStatus) -> Self {
match value {
CStatus::Success => HciHalStatus::Success,
CStatus::AlreadyInitialized => HciHalStatus::AlreadyInitialized,
CStatus::UnableToOpenInterface => HciHalStatus::UnableToOpenInterface,
CStatus::HardwareInitializationError => HciHalStatus::HardwareInitializationError,
CStatus::Unknown => HciHalStatus::Unknown,
}
}
}