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, _death_recipient: DeathRecipient, }, } struct ServiceState { client_state: ClientState, writer: fs::File, } pub struct UwbChip { name: String, _handle: tokio::task::JoinHandle<()>, service_state: Arc>, } /// Configure a file descriptor as raw fd. pub fn makeraw(file: fs::File) -> std::io::Result { 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 { Ok(self.name.clone()) } async fn open(&self, callbacks: &Strong) -> 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 { log::debug!("getSupportedAndroidUciVersion"); Ok(1) } async fn sendUciMessage(&self, data: &[u8]) -> Result { 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()) } }