Skip to content

Commit

Permalink
implement blocking trait for external controller
Browse files Browse the repository at this point in the history
  • Loading branch information
lulf committed May 13, 2024
1 parent db427d5 commit b2f1a1f
Showing 1 changed file with 101 additions and 19 deletions.
120 changes: 101 additions & 19 deletions src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::cmd::{Cmd, CmdReturnBuf};
use crate::event::{CommandComplete, Event};
use crate::param::{RemainingBytes, Status};
use crate::transport::Transport;
use crate::{cmd, data, ControllerToHostPacket, FixedSizeValue, FromHciBytes, HostToControllerPacket};
use crate::{cmd, data, ControllerToHostPacket, FixedSizeValue, FromHciBytes};

pub mod blocking;

Expand Down Expand Up @@ -45,33 +45,23 @@ pub trait ControllerCmdAsync<C: cmd::AsyncCmd + ?Sized>: Controller {
///
/// The contract is that before sending a command, a slot is reserved, which
/// returns a signal handle that can be used to await a response.
pub struct ExternalController<T, const SLOTS: usize>
where
T: Transport,
{
pub struct ExternalController<T, const SLOTS: usize> {
transport: T,
slots: ControllerState<SLOTS>,
}

impl<T, const SLOTS: usize> ExternalController<T, SLOTS>
where
T: Transport,
{
impl<T, const SLOTS: usize> ExternalController<T, SLOTS> {
pub fn new(transport: T) -> Self {
Self {
slots: ControllerState::new(),
transport,
}
}

async fn write<W: HostToControllerPacket>(&self, data: &W) -> Result<(), T::Error> {
self.transport.write(data).await
}
}

impl<T, const SLOTS: usize> ErrorType for ExternalController<T, SLOTS>
where
T: Transport,
T: ErrorType,
{
type Error = T::Error;
}
Expand All @@ -81,17 +71,17 @@ where
T: Transport,
{
async fn write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), Self::Error> {
self.write(packet).await?;
self.transport.write(packet).await?;
Ok(())
}

async fn write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), Self::Error> {
self.write(packet).await?;
self.transport.write(packet).await?;
Ok(())
}

async fn write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), Self::Error> {
self.write(packet).await?;
self.transport.write(packet).await?;
Ok(())
}

Expand Down Expand Up @@ -126,6 +116,98 @@ where
}
}

impl<T, const SLOTS: usize> blocking::Controller for ExternalController<T, SLOTS>
where
T: crate::transport::blocking::Transport,
{
fn write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), Self::Error> {
loop {
match self.try_write_acl_data(packet) {
Err(blocking::TryError::Busy) => {}
Err(blocking::TryError::Error(e)) => return Err(e),
Ok(r) => return Ok(r),
}
}
}

fn write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), Self::Error> {
loop {
match self.try_write_sync_data(packet) {
Err(blocking::TryError::Busy) => {}
Err(blocking::TryError::Error(e)) => return Err(e),
Ok(r) => return Ok(r),
}
}
}

fn write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), Self::Error> {
loop {
match self.try_write_iso_data(packet) {
Err(blocking::TryError::Busy) => {}
Err(blocking::TryError::Error(e)) => return Err(e),
Ok(r) => return Ok(r),
}
}
}

fn read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, Self::Error> {
loop {
// Safety: we will not hold references across loop iterations.
let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) };
match self.try_read(buf) {
Err(blocking::TryError::Busy) => {}
Err(blocking::TryError::Error(e)) => return Err(e),
Ok(r) => return Ok(r),
}
}
}

fn try_write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
self.transport.write(packet)?;
Ok(())
}

fn try_write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
self.transport.write(packet)?;
Ok(())
}

fn try_write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
self.transport.write(packet)?;
Ok(())
}

fn try_read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, blocking::TryError<Self::Error>> {
loop {
{
// Safety: we will not hold references across loop iterations.
let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) };
let value = self.transport.read(&mut buf[..])?;
match value {
ControllerToHostPacket::Event(ref event) => match &event {
Event::CommandComplete(e) => {
self.slots.complete(
e.cmd_opcode,
e.status,
e.num_hci_cmd_pkts as usize,
e.return_param_bytes.as_ref(),
);
continue;
}
Event::CommandStatus(e) => {
self.slots
.complete(e.cmd_opcode, e.status, e.num_hci_cmd_pkts as usize, &[]);
continue;
}
_ => return Ok(value),
},
_ => return Ok(value),
}
}
}
}
}

impl<T, C, const SLOTS: usize> ControllerCmdSync<C> for ExternalController<T, SLOTS>
where
T: Transport,
Expand All @@ -141,7 +223,7 @@ where
self.slots.release_slot(idx);
});

self.write(cmd).await.map_err(cmd::Error::Io)?;
self.transport.write(cmd).await.map_err(cmd::Error::Io)?;

let result = slot.wait().await;
let return_param_bytes = RemainingBytes::from_hci_bytes_complete(&retval.as_ref()[..result.len]).unwrap();
Expand All @@ -168,7 +250,7 @@ where
self.slots.release_slot(idx);
});

self.write(cmd).await.map_err(cmd::Error::Io)?;
self.transport.write(cmd).await.map_err(cmd::Error::Io)?;

let result = slot.wait().await;
result.status.to_result()?;
Expand Down

0 comments on commit b2f1a1f

Please sign in to comment.