// Copyright 2021, 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. //! Implementation of the HAl that talks to NFC controller over Android's HIDL use crate::internal::InnerHal; #[allow(unused)] use crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result}; use log::{debug, error}; use nfc_packets::nci::{DataPacket, NciPacket}; use pdl_runtime::Packet; use std::sync::Mutex; use tokio::select; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::sync::oneshot; /// Initialize the module pub async fn init() -> Hal { let (raw_hal, inner_hal) = InnerHal::new(); let (hal_open_evt_tx, hal_open_evt_rx) = oneshot::channel::(); let (hal_close_evt_tx, hal_close_evt_rx) = oneshot::channel::(); *CALLBACKS.lock().unwrap() = Some(Callbacks { hal_open_evt_tx: Some(hal_open_evt_tx), hal_close_evt_tx: Some(hal_close_evt_tx), in_cmd_tx: inner_hal.in_cmd_tx, in_data_tx: inner_hal.in_data_tx, }); ffi::start_hal(); hal_open_evt_rx.await.unwrap(); tokio::spawn(dispatch_outgoing( raw_hal.hal_events.clone(), inner_hal.out_cmd_rx, inner_hal.out_data_rx, hal_close_evt_rx, )); raw_hal } #[cxx::bridge(namespace = nfc::hal)] // TODO Either use or remove these functions, this shouldn't be the long term state #[allow(dead_code)] #[allow(unsafe_op_in_unsafe_fn)] mod ffi { #[repr(u32)] #[derive(Debug)] enum NfcEvent { OPEN_CPLT = 0, CLOSE_CPLT = 1, POST_INIT_CPLT = 2, PRE_DISCOVER_CPLT = 3, REQUEST_CONTROL = 4, RELEASE_CONTROL = 5, ERROR = 6, HCI_NETWORK_RESET = 7, } #[repr(u32)] #[derive(Debug)] enum NfcStatus { OK = 0, FAILED = 1, ERR_TRANSPORT = 2, ERR_CMD_TIMEOUT = 3, REFUSED = 4, } unsafe extern "C++" { include!("hal/ffi/hidl.h"); fn start_hal(); fn stop_hal(); fn send_command(data: &[u8]); #[namespace = "android::hardware::nfc::V1_1"] type NfcEvent; #[namespace = "android::hardware::nfc::V1_0"] type NfcStatus; } extern "Rust" { fn on_event(evt: NfcEvent, status: NfcStatus); fn on_data(data: &[u8]); } } impl From for HalEventStatus { fn from(ffi_nfc_status: ffi::NfcStatus) -> Self { match ffi_nfc_status { ffi::NfcStatus::OK => HalEventStatus::Success, ffi::NfcStatus::FAILED => HalEventStatus::Failed, ffi::NfcStatus::ERR_TRANSPORT => HalEventStatus::TransportError, ffi::NfcStatus::ERR_CMD_TIMEOUT => HalEventStatus::Timeout, ffi::NfcStatus::REFUSED => HalEventStatus::Refused, _ => HalEventStatus::Failed, } } } struct Callbacks { hal_open_evt_tx: Option>, hal_close_evt_tx: Option>, in_cmd_tx: UnboundedSender, in_data_tx: UnboundedSender, } static CALLBACKS: Mutex> = Mutex::new(None); fn on_event(evt: ffi::NfcEvent, status: ffi::NfcStatus) { debug!("got event: {:?} with status {:?}", evt, status); let mut callbacks = CALLBACKS.lock().unwrap(); match evt { ffi::NfcEvent::OPEN_CPLT => { if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_open_evt_tx.take() { evt_tx.send(status).unwrap(); } } ffi::NfcEvent::CLOSE_CPLT => { if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_close_evt_tx.take() { evt_tx.send(status).unwrap(); } } _ => error!("Unhandled HAL event {:?}", evt), } } fn on_data(data: &[u8]) { debug!("got packet: {:02x?}", data); let callbacks = CALLBACKS.lock().unwrap(); if is_control_packet(data) { match NciPacket::parse(data) { Ok(p) => callbacks.as_ref().unwrap().in_cmd_tx.send(p).unwrap(), Err(e) => error!("failure to parse response: {:?} data: {:02x?}", e, data), } } else { match DataPacket::parse(data) { Ok(p) => callbacks.as_ref().unwrap().in_data_tx.send(p).unwrap(), Err(e) => error!("failure to parse response: {:?} data: {:02x?}", e, data), } } } async fn dispatch_outgoing( mut hal_events: HalEventRegistry, mut out_cmd_rx: UnboundedReceiver, mut out_data_rx: UnboundedReceiver, hal_close_evt_rx: oneshot::Receiver, ) { loop { select! { Some(cmd) = out_cmd_rx.recv() => ffi::send_command(&cmd.encode_to_bytes().unwrap()), Some(data) = out_data_rx.recv() => ffi::send_command(&data.encode_to_bytes().unwrap()), else => break, } } ffi::stop_hal(); let status = hal_close_evt_rx.await.unwrap(); if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await { evt.send(HalEventStatus::from(status)).unwrap(); } }