diff --git a/src/controller.rs b/src/controller.rs index 00e5d0e..3e64663 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -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; @@ -45,33 +45,23 @@ pub trait ControllerCmdAsync: 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 -where - T: Transport, -{ +pub struct ExternalController { transport: T, slots: ControllerState, } -impl ExternalController -where - T: Transport, -{ +impl ExternalController { pub fn new(transport: T) -> Self { Self { slots: ControllerState::new(), transport, } } - - async fn write(&self, data: &W) -> Result<(), T::Error> { - self.transport.write(data).await - } } impl ErrorType for ExternalController where - T: Transport, + T: ErrorType, { type Error = T::Error; } @@ -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(()) } @@ -126,6 +116,98 @@ where } } +impl blocking::Controller for ExternalController +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, 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.transport.write(packet)?; + Ok(()) + } + + fn try_write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), blocking::TryError> { + self.transport.write(packet)?; + Ok(()) + } + + fn try_write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), blocking::TryError> { + self.transport.write(packet)?; + Ok(()) + } + + fn try_read<'a>(&self, buf: &'a mut [u8]) -> Result, blocking::TryError> { + 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 ControllerCmdSync for ExternalController where T: Transport, @@ -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(); @@ -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()?;