blob: 0ed05d85cad4a2371eb5d83016d14f24859f492f [file] [log] [blame]
use android_hardware_uwb::aidl::android::hardware::uwb::{
IUwbChip::IUwbChipAsyncServer, IUwbClientCallback::IUwbClientCallback, UwbEvent::UwbEvent,
UwbStatus::UwbStatus,
};
use android_hardware_uwb::binder;
use async_trait::async_trait;
use binder::{DeathRecipient, IBinder, Result, Strong};
use std::sync::Arc;
use tokio::fs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::Mutex;
enum ClientState {
Closed,
Opened {
callbacks: Strong<dyn IUwbClientCallback>,
_death_recipient: DeathRecipient,
},
}
struct ServiceState {
client_state: ClientState,
writer: fs::File,
}
pub struct UwbChip {
name: String,
_handle: tokio::task::JoinHandle<()>,
service_state: Arc<Mutex<ServiceState>>,
}
/// Configure a file descriptor as raw fd.
pub fn makeraw(file: fs::File) -> std::io::Result<fs::File> {
use nix::sys::termios::*;
let mut attrs = tcgetattr(&file)?;
cfmakeraw(&mut attrs);
tcsetattr(&file, SetArg::TCSANOW, &attrs)?;
Ok(file)
}
impl UwbChip {
pub async fn new(name: String, path: String) -> Self {
// Open the serial file and configure it as raw file
// descriptor.
let mut reader = fs::OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(&path)
.await
.and_then(makeraw)
.expect("failed to open the serial device");
let writer = reader
.try_clone()
.await
.expect("failed to clone serial for writing");
// Create the chip
let service_state = Arc::new(Mutex::new(ServiceState {
writer,
client_state: ClientState::Closed,
}));
// Spawn the task that will run the polling loop.
let handle = {
let service_state = service_state.clone();
tokio::task::spawn(async move {
log::info!("UCI reader task started");
const MESSAGE_TYPE_MASK: u8 = 0b11100000;
const DATA_MESSAGE_TYPE: u8 = 0b000;
const UCI_HEADER_SIZE: usize = 4;
const UCI_BUFFER_SIZE: usize = 1024;
let mut buffer = [0; UCI_BUFFER_SIZE];
loop {
reader
.read_exact(&mut buffer[0..UCI_HEADER_SIZE])
.await
.expect("failed to read uci header bytes");
let common_header = buffer[0];
let mt = (common_header & MESSAGE_TYPE_MASK) >> 5;
let payload_length = if mt == DATA_MESSAGE_TYPE {
u16::from_le_bytes([buffer[2], buffer[3]]) as usize
} else {
buffer[3] as usize
};
let total_packet_length = payload_length + UCI_HEADER_SIZE;
reader
.read_exact(&mut buffer[UCI_HEADER_SIZE..total_packet_length])
.await
.expect("failed to read uci payload bytes");
log::debug!(" <-- {:?}", &buffer[0..total_packet_length]);
let service_state = service_state.lock().await;
if let ClientState::Opened { ref callbacks, .. } = service_state.client_state {
callbacks
.onUciMessage(&buffer[0..total_packet_length])
.unwrap();
}
}
})
};
Self {
name,
_handle: handle,
service_state,
}
}
}
impl binder::Interface for UwbChip {}
#[async_trait]
impl IUwbChipAsyncServer for UwbChip {
async fn getName(&self) -> Result<String> {
Ok(self.name.clone())
}
async fn open(&self, callbacks: &Strong<dyn IUwbClientCallback>) -> Result<()> {
log::debug!("open");
let mut service_state = self.service_state.lock().await;
if matches!(service_state.client_state, ClientState::Opened { .. }) {
log::error!("the state is already opened");
return Err(binder::ExceptionCode::ILLEGAL_STATE.into());
}
let mut death_recipient = {
let service_state = self.service_state.clone();
DeathRecipient::new(move || {
log::info!("Uwb service has died");
let mut service_state = service_state.blocking_lock();
service_state.client_state = ClientState::Closed;
})
};
callbacks.as_binder().link_to_death(&mut death_recipient)?;
callbacks.onHalEvent(UwbEvent::OPEN_CPLT, UwbStatus::OK)?;
service_state.client_state = ClientState::Opened {
callbacks: callbacks.clone(),
_death_recipient: death_recipient,
};
Ok(())
}
async fn close(&self) -> Result<()> {
log::debug!("close");
let mut service_state = self.service_state.lock().await;
if matches!(service_state.client_state, ClientState::Closed) {
log::error!("the state is already closed");
return Err(binder::ExceptionCode::ILLEGAL_STATE.into());
}
// Send the command Device Reset to stop all running activities
// on the UWBS emulator. This is necessary because the emulator
// is otherwise not notified of the power down (the serial stays
// open).
//
// The response to the command will be dropped by the polling loop,
// as the callbacks will have been removed then.
let uci_core_device_reset_cmd = [0x20, 0x00, 0x00, 0x01, 0x00];
service_state
.writer
.write_all(&uci_core_device_reset_cmd)
.await
.expect("failed to write UCI Device Reset command");
if let ClientState::Opened { ref callbacks, .. } = service_state.client_state {
callbacks.onHalEvent(UwbEvent::CLOSE_CPLT, UwbStatus::OK)?;
}
service_state.client_state = ClientState::Closed;
Ok(())
}
async fn coreInit(&self) -> Result<()> {
log::debug!("coreInit");
let service_state = self.service_state.lock().await;
if let ClientState::Opened { ref callbacks, .. } = service_state.client_state {
callbacks.onHalEvent(UwbEvent::POST_INIT_CPLT, UwbStatus::OK)?;
Ok(())
} else {
Err(binder::ExceptionCode::ILLEGAL_STATE.into())
}
}
async fn sessionInit(&self, _id: i32) -> Result<()> {
log::debug!("sessionInit");
Ok(())
}
async fn getSupportedAndroidUciVersion(&self) -> Result<i32> {
log::debug!("getSupportedAndroidUciVersion");
Ok(1)
}
async fn sendUciMessage(&self, data: &[u8]) -> Result<i32> {
log::debug!("sendUciMessage");
let mut service_state = self.service_state.lock().await;
if matches!(service_state.client_state, ClientState::Closed) {
log::error!("the state is not opened");
return Err(binder::ExceptionCode::ILLEGAL_STATE.into());
}
log::debug!(" --> {:?}", data);
service_state
.writer
.write_all(data)
.await
.map(|_| data.len() as i32)
.map_err(|_| binder::StatusCode::UNKNOWN_ERROR.into())
}
}