blob: 2e8283a7ee9d197849a1c51296af8766d5896f58 [file]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::{format_err, Context as _, Error},
fidl_fuchsia_lowpan_spinel::{
DeviceEvent, DeviceEventStream, DeviceProxy, Error as SpinelError,
},
fuchsia_async::futures::stream::TryStreamExt,
fuchsia_syslog::macros::*,
};
pub const OT_RADIO_RESET_EVENT: &[u8; 3] = &[0x80, 0x06, 0x0];
pub const OT_RADIO_TX_ALLOWANCE_INIT_VAL: u32 = 4;
pub const OT_RADIO_TX_ALLOWANCE_LOW_THRESHOLD_VAL: u32 = 2;
pub const OT_RADIO_TX_ALLOWANCE_HIGH_THRESHOLD_VAL: u32 = 4;
pub const OT_RADIO_RX_ALLOWANCE_INIT_VAL: u32 = 4;
pub const OT_RADIO_RX_ALLOWANCE_INC_VAL: u32 = 2;
pub const OT_RADIO_GET_FRAME_AND_TX_ALLOWANCE: u32 = 2;
pub const OT_RADIO_GET_FRAME: u32 = 1;
pub async fn expect_on_receive_event(stream: &mut DeviceEventStream) -> Result<Vec<u8>, Error> {
if let Some(event) = stream.try_next().await.context("error waiting for spinel events")? {
match event {
DeviceEvent::OnReceiveFrame { data } => {
return Ok(data);
}
DeviceEvent::OnReadyForSendFrames { number_of_frames } => {
return Err(format_err!(
"Expect OnReceive(), got OnReadyForSendFrame(): {:?}",
number_of_frames
));
}
DeviceEvent::OnError { error, .. } => {
return Err(format_err!("Expect OnReceive(), got OnError(): {:?}", error));
}
}
}
return Err(format_err!("No event"));
}
pub async fn expect_on_ready_for_send_frame_event(
stream: &mut DeviceEventStream,
) -> Result<u32, Error> {
if let Some(event) = stream.try_next().await.context("error waiting for spinel events")? {
match event {
DeviceEvent::OnReceiveFrame { data } => {
return Err(format_err!(
"Expect OnReadyForSendFrames(), got OnReceiveFrame(): {:?}",
data
));
}
DeviceEvent::OnReadyForSendFrames { number_of_frames } => {
return Ok(number_of_frames);
}
DeviceEvent::OnError { error, .. } => {
return Err(format_err!(
"Expect OnReadyForSendFrames(), got OnError(): {:?}",
error
));
}
}
}
return Err(format_err!("No event"));
}
pub async fn expect_on_error_event(
stream: &mut DeviceEventStream,
expected_err: SpinelError,
) -> Result<(), Error> {
if let Some(event) = stream.try_next().await.context("error waiting for spinel events")? {
match event {
DeviceEvent::OnReceiveFrame { data } => {
return Err(format_err!("Expect OnError(), got OnReceiveFrame(): {:?}", data));
}
DeviceEvent::OnReadyForSendFrames { number_of_frames } => {
return Err(format_err!(
"Expect OnError(), got OnReadyForSendFrame(): {:?}",
number_of_frames
));
}
DeviceEvent::OnError { error, .. } => {
if error != expected_err {
return Err(format_err!("Expect Err {:?}, got Err {:?}", error, expected_err));
}
return Ok(());
}
}
}
return Err(format_err!("No event"));
}
pub struct OtRadioDevice {
pub tx_allowance: u32,
pub rx_allowance: u32,
}
impl<'a> OtRadioDevice {
pub async fn expect_one_rx_frame_event(
&mut self,
stream: &mut DeviceEventStream,
num_of_event: u32,
) -> Result<Vec<u8>, anyhow::Error> {
let mut res: Vec<u8> = vec![];
fx_log_info!("waiting for {} frame", num_of_event);
for _ in 0..num_of_event {
if let Some(event) =
stream.try_next().await.context("error waiting for spinel events")?
{
match event {
DeviceEvent::OnReceiveFrame { data } => {
res = data;
self.rx_allowance -= 1;
}
DeviceEvent::OnReadyForSendFrames { number_of_frames } => {
self.tx_allowance += number_of_frames;
}
DeviceEvent::OnError { error, .. } => {
return Err(format_err!("received error code {:?}", error));
}
}
}
}
return Ok(res);
}
}
pub fn validate_reset_frame(frame_slice: &[u8]) {
assert_eq!(OT_RADIO_RESET_EVENT, &frame_slice[..3]);
assert!((frame_slice[3] & 0xF0) == 0x70);
}
/// A LoWPAN Spinel Device Client implementation that uses for integration testing only.
pub struct LoWPANSpinelDeviceClientImpl {
pub tx_allowance: u32,
pub rx_allowance: u32,
pub stream: DeviceEventStream,
pub device_proxy: DeviceProxy,
}
impl<'a> LoWPANSpinelDeviceClientImpl {
pub fn new(device_proxy_in: DeviceProxy) -> Self {
let device_proxy_event_stream = device_proxy_in.take_event_stream();
Self {
tx_allowance: 0,
rx_allowance: 0,
stream: device_proxy_event_stream,
device_proxy: device_proxy_in,
}
}
pub async fn open_and_init_device(&mut self) {
// Initialize ot-radio device.
self.device_proxy.open().await.expect("sending FIDL req").expect("getting FIDL resp");
// Get allowance for this component to send out frames
self.tx_allowance =
expect_on_ready_for_send_frame_event(&mut self.stream).await.expect("receiving frame");
// Let spinel device know that this component is ready to receive frames
self.device_proxy
.ready_to_receive_frames(OT_RADIO_RX_ALLOWANCE_INIT_VAL)
.expect("setting receive frame num");
self.rx_allowance = OT_RADIO_RX_ALLOWANCE_INIT_VAL;
// Receive and validate the reset event frame.
let rx_frame = expect_on_receive_event(&mut self.stream).await.expect("receiving event");
fx_log_info!("received frame {:?}", rx_frame);
validate_reset_frame(&rx_frame);
self.rx_allowance -= 1;
}
async fn expect_device_event_for_rx_frame(
&mut self,
num_of_event: u32,
) -> Result<Vec<u8>, anyhow::Error> {
let mut res: Vec<u8> = vec![];
for _ in 0..num_of_event {
if let Some(event) =
self.stream.try_next().await.context("error waiting for spinel events")?
{
match event {
DeviceEvent::OnReceiveFrame { data } => {
res = data;
self.rx_allowance -= 1;
}
DeviceEvent::OnReadyForSendFrames { number_of_frames } => {
self.tx_allowance += number_of_frames;
}
DeviceEvent::OnError { error, .. } => {
return Err(format_err!("received error code {:?}", error));
}
}
}
}
return Ok(res);
}
pub async fn receive_one_frame(&mut self) -> Result<Vec<u8>, anyhow::Error> {
let ret_val: Vec<u8>;
if self.rx_allowance == 0 {
return Err(format_err!("no allowance for receiving frame"));
}
// If tx_allowance is in the lower threshold, the test component expects to receive an
// OnReadyForSendFrame event and a frame. This prevents the component sending frames
// too fast.
if self.tx_allowance == OT_RADIO_TX_ALLOWANCE_LOW_THRESHOLD_VAL {
ret_val =
self.expect_device_event_for_rx_frame(OT_RADIO_GET_FRAME_AND_TX_ALLOWANCE).await?;
} else {
ret_val = self.expect_device_event_for_rx_frame(OT_RADIO_GET_FRAME).await?;
}
if !(self.tx_allowance > OT_RADIO_TX_ALLOWANCE_LOW_THRESHOLD_VAL
&& self.tx_allowance <= OT_RADIO_TX_ALLOWANCE_HIGH_THRESHOLD_VAL)
{
return Err(format_err!("invalid value for tx_allowance"));
}
// If rx_allowance is even, the component is ready to receive two more frames.
if (self.rx_allowance & 1) == 0 {
self.device_proxy.ready_to_receive_frames(OT_RADIO_RX_ALLOWANCE_INC_VAL)?;
self.rx_allowance += OT_RADIO_RX_ALLOWANCE_INC_VAL;
}
return Ok(ret_val);
}
pub async fn send_one_frame(&mut self, frame: &[u8]) -> Result<(), anyhow::Error> {
if self.tx_allowance == 0 {
return Err(format_err!("no allowance for sending frame"));
}
self.device_proxy.send_frame(frame).context("sending frame")?;
self.tx_allowance -= 1;
return Ok(());
}
}