blob: 83aadcb7908e0e521ee4e6229f455c63e37f946b [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::hal_bindings;
use async_trait::async_trait;
use lazy_static::lazy_static;
use log::{debug, error};
use scopeguard::defer;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use uwb_core::error::{Error as UwbError, Result as UwbResult};
use uwb_core::params::uci_packets::SessionId;
use uwb_core::uci::{UciHal, UciHalPacket};
/// Struct used to send Event-id and Status received from the HAL as
/// a single message across the channel.
#[derive(Debug)]
struct HalEvent {
event_id: u32,
status: u32,
}
/// Struct used to protect an UnboundedSender used to send HAL events
/// or data within a mutex.
struct HalSender<T> {
sender: Arc<Mutex<Option<mpsc::UnboundedSender<T>>>>,
}
impl<T> HalSender<T> {
/// Stores the sender.
fn set(&self, sender: mpsc::UnboundedSender<T>) {
*self.sender.lock().unwrap() = Some(sender);
}
/// Clears the sender.
fn clear(&self) {
*self.sender.lock().unwrap() = None;
}
/// Sends a packet over the channel using the stored sender.
fn send(&self, packet: T) -> Result<(), ()> {
if let Some(sender) = self.sender.lock().unwrap().as_ref() {
if let Err(err) = sender.send(packet) {
error!("UCI_HAL: Send failed: {}", err);
return Err(());
}
} else {
error!("UCI_HAL: Hal Sender is None");
return Err(());
}
Ok(())
}
}
lazy_static! {
// Sender used by the callback registered with the HAL to send events containing the status
// of the requested open, init and close operations.
static ref HAL_EVT_SENDER: HalSender<HalEvent> =
HalSender { sender: Arc::new(Mutex::new(None)) };
// Sender used by the callback registered with the HAL to send UCI packets.
// static mut uci_packet_tx: Option<mpsc::UnboundedSender<UciHalPacket>> = None;
static ref UCI_PACKET_SENDER: HalSender<Vec<u8>> =
HalSender { sender: Arc::new(Mutex::new(None)) };
}
/// Callback function registered with the HAL to receive events containing the status of the
/// requested operation.
extern "C" fn hal_event_status_cb(
event: hal_bindings::uwb_event_t,
event_status: hal_bindings::uwb_status_t,
) {
let hal_event: HalEvent = HalEvent {
event_id: u32::from(event),
status: u32::from(event_status),
};
if HAL_EVT_SENDER.send(hal_event).is_err() {
error!(
"UCI_HAL: Error sending event:{} status:{}",
event, event_status
);
}
}
/// Returns a Vec created from a raw pointer to a buffer.
///
/// # Safety
///
/// ptr_to_data must be non-null.
///
/// ptr_to_data must point to data_len consecutive properly initialized values of
/// type u8.
///
/// Adding data_len to ptr_to_data must not wrap around the address space.
unsafe fn ptr_to_vec(ptr_to_data: *const u8, data_len: u16) -> Vec<u8> {
let data_slice: &[u8] = std::slice::from_raw_parts(ptr_to_data as *mut u8, data_len as usize);
data_slice.to_vec()
}
/// Callback function registered with the HAL to receive UCI packets.
extern "C" fn hal_data_cb(data_len: u16, p_data: *mut u8) {
// Check for null.
if p_data.is_null() {
error!("UCI_HAL: p_data is NULL");
return;
}
// Check for wrap around.
if p_data.wrapping_add(data_len.into()) < p_data {
error!("UCI_HAL: Data wraps around address space");
return;
}
let data_vec: Vec<u8>;
// Unsafe because we need to access and read in the data from a raw pointer.
// SAFETY: We just checked that p_data is not null and
// that the data being accessed does not wrap around
// the address space.
unsafe {
data_vec = ptr_to_vec(p_data, data_len);
}
if UCI_PACKET_SENDER.send(data_vec).is_err() {
error!("UCI_HAL: Failed to send UCI packet");
}
}
/// Initializes the UWB hardware via the interface exposed by the HAL.
async fn hal_core_init() -> UwbResult<()> {
debug!("UCI HAL: Core init");
let (init_sender, mut init_receiver) = mpsc::unbounded_channel();
let hal_core_init_result: u16;
HAL_EVT_SENDER.set(init_sender);
defer! {
HAL_EVT_SENDER.clear();
}
// Unsafe because the function being called is defined in the HAL library that is
// implemented in C and not in Rust.
unsafe {
hal_core_init_result = hal_bindings::phNxpUciHal_coreInitialization();
}
if u32::from(hal_core_init_result) != hal_bindings::NxpUwbHalStatus_HAL_STATUS_OK {
error!(
"UCI HAL: Error initializing NXP HAL: 0x{:X}",
hal_core_init_result
);
return Err(UwbError::ForeignFunctionInterface);
}
if let Some(hal_evt) = init_receiver.recv().await {
if hal_evt.event_id == hal_bindings::UWB_EVT_HAL_UWB_INIT_CPLT_EVT
&& hal_evt.status == hal_bindings::UWB_EVT_STATUS_HAL_UWB_STATUS_OK
{
debug!("UCI HAL: Core init succesful");
Ok(())
} else {
error!(
"UCI HAL: Error initializing NXP HAL: Event: {}
Status: {}",
hal_evt.event_id, hal_evt.status
);
Err(UwbError::ForeignFunctionInterface)
}
} else {
error!("UCI HAL: Error receiving message on init_receiver");
Err(UwbError::ForeignFunctionInterface)
}
}
/// Initializes the UWB HAL and the hardware.
async fn hal_open() -> UwbResult<()> {
debug!("UCI HAL: Open");
let (open_sender, mut open_receiver) = mpsc::unbounded_channel();
let hal_open_result: u16;
HAL_EVT_SENDER.set(open_sender);
defer! {
HAL_EVT_SENDER.clear();
}
// Unsafe because the function being called is defined in the HAL library that is
// implemented in C and not in Rust.
unsafe {
hal_open_result =
hal_bindings::phNxpUciHal_open(Some(hal_event_status_cb), Some(hal_data_cb));
}
if u32::from(hal_open_result) != hal_bindings::NxpUwbHalStatus_HAL_STATUS_OK {
error!("UCI HAL: Error opening NXP HAL: 0x{:X}", hal_open_result);
HAL_EVT_SENDER.clear();
return Err(UwbError::ForeignFunctionInterface);
}
if let Some(hal_open_evt) = open_receiver.recv().await {
if hal_open_evt.event_id == hal_bindings::UWB_EVT_HAL_UWB_OPEN_CPLT_EVT
&& hal_open_evt.status == hal_bindings::UWB_EVT_STATUS_HAL_UWB_STATUS_OK
{
debug!("UCI HAL: Open successful");
hal_core_init().await
} else {
error!(
"UCI HAL: Error opening NXP HAL: Event: {} Status: {}",
hal_open_evt.event_id, hal_open_evt.status
);
Err(UwbError::ForeignFunctionInterface)
}
} else {
error!("UCI HAL: Error receiving message on open_receiver");
Err(UwbError::ForeignFunctionInterface)
}
}
/// A UciHal implementation for ChromeOS.
pub struct UciHalImpl {}
#[async_trait]
impl UciHal for UciHalImpl {
async fn open(&mut self, packet_sender: mpsc::UnboundedSender<UciHalPacket>) -> UwbResult<()> {
// Save the packet sender. This handle is used to send UCI messages to the higher layers.
UCI_PACKET_SENDER.set(packet_sender);
hal_open().await
}
async fn close(&mut self) -> UwbResult<()> {
debug!("UCI HAL: Close");
let (close_sender, mut close_receiver) = mpsc::unbounded_channel();
let hal_close_result: u16;
HAL_EVT_SENDER.set(close_sender);
defer! {
HAL_EVT_SENDER.clear();
}
// Unsafe because the function being called is defined in the HAL library that is
// implemented in C and not in Rust.
unsafe {
hal_close_result = hal_bindings::phNxpUciHal_close();
}
if u32::from(hal_close_result) != hal_bindings::NxpUwbHalStatus_HAL_STATUS_OK {
error!("UCI HAL: Error closing NXP HAL: 0x{:X}", hal_close_result);
return Err(UwbError::ForeignFunctionInterface);
}
if let Some(hal_close_evt) = close_receiver.recv().await {
if hal_close_evt.event_id == hal_bindings::UWB_EVT_HAL_UWB_CLOSE_CPLT_EVT
&& hal_close_evt.status == hal_bindings::UWB_EVT_STATUS_HAL_UWB_STATUS_OK
{
debug!("UCI HAL: Close successful");
Ok(())
} else {
error!(
"UCI HAL: Error closing NXP HAL: Event: {} Status: {}",
hal_close_evt.event_id, hal_close_evt.status
);
Err(UwbError::ForeignFunctionInterface)
}
} else {
error!("UCI HAL: Error receiving message on close_receiver");
Err(UwbError::ForeignFunctionInterface)
}
}
async fn send_packet(&mut self, packet: UciHalPacket) -> UwbResult<()> {
debug!(
"UCI HAL: send_packet len:{}; packet:{:?}",
packet.len(),
packet
);
let hal_write_result: u16;
let packet_len: Result<u16, <u16 as TryFrom<usize>>::Error> = packet.len().try_into();
if let Ok(len) = packet_len {
// Unsafe because the function being called is defined in the HAL library that is
// implemented in C and not in Rust.
// SAFETY: We ensured that the len being passed in is a valid u16 converted
// from usize
unsafe {
hal_write_result = hal_bindings::phNxpUciHal_write(len, packet.as_ptr());
}
if usize::from(hal_write_result) == packet.len() {
Ok(())
} else {
error!(
"UCI HAL: Error writing to NXP HAL: 0x{:X}",
hal_write_result
);
Err(UwbError::ForeignFunctionInterface)
}
} else {
error!("UCI HAL: Invalid packet length: {}", packet.len());
Err(UwbError::ForeignFunctionInterface)
}
}
async fn notify_session_initialized(&mut self, session_id: SessionId) -> UwbResult<()> {
debug!(
"UCI HAL: notify_session_initialized session_id: {}",
session_id
);
let hal_result: u16;
// Unsafe because the function being called is defined in the HAL library
// that is implemented in C and not in Rust.
unsafe {
hal_result = hal_bindings::phNxpUciHal_sessionInitialization(session_id);
}
if u32::from(hal_result) == hal_bindings::NxpUwbHalStatus_HAL_STATUS_OK {
Ok(())
} else {
error!("UCI HAL: Error initializing session: 0x{:X}", hal_result);
Err(UwbError::ForeignFunctionInterface)
}
}
}