use std::{
io::{Read, Write},
marker::PhantomData,
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
descriptor::PipeInfo, ffi, overlapped::Overlapped, try_d3xx, util::PhantomLifetime, D3xxError,
Device, Result,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PipeIo<'a> {
handle: ffi::FT_HANDLE,
id: Pipe,
_lifetime_constraint: PhantomLifetime<'a>,
}
impl<'a> PipeIo<'a> {
#[must_use]
pub fn new(device: &'a Device, id: Pipe) -> Self {
Self {
handle: device.handle(),
id,
_lifetime_constraint: PhantomData,
}
}
#[must_use]
pub fn id(&self) -> Pipe {
self.id
}
pub fn descriptor(&self) -> Result<PipeInfo> {
const INTERFACE_INDEX: ffi::UCHAR = 1;
let mut info = ffi::FT_PIPE_INFORMATION::default();
try_d3xx!(unsafe {
ffi::FT_GetPipeInformation(
self.handle,
INTERFACE_INDEX,
ffi::UCHAR::from(self.id),
&mut info,
)
})?;
PipeInfo::new(info)
}
pub fn set_stream_size(&self, size: Option<usize>) -> Result<()> {
#[cfg(windows)]
type Bool = ffi::BOOLEAN;
#[cfg(not(windows))]
type Bool = ffi::BOOL;
match size {
Some(size) => {
try_d3xx!(unsafe {
ffi::FT_SetStreamPipe(
self.handle,
Bool::from(false),
Bool::from(false),
self.id as ffi::UCHAR,
size.try_into().or(Err(D3xxError::InvalidArgs))?,
)
})
}
None => {
try_d3xx!(unsafe {
ffi::FT_ClearStreamPipe(
self.handle,
Bool::from(false),
Bool::from(false),
self.id as ffi::UCHAR,
)
})
}
}
}
pub fn abort(&self) -> Result<()> {
try_d3xx!(unsafe { ffi::FT_AbortPipe(self.handle, u8::from(self.id)) })
}
fn maybe_abort<T>(&self, res: Result<T>) -> Result<T> {
res.map_err(|e| {
let _ = self.abort();
e
})
}
#[cfg(windows)]
pub fn timeout(&self) -> Result<u32> {
let mut timeout = 0;
try_d3xx!(unsafe { ffi::FT_GetPipeTimeout(self.handle, u8::from(self.id), &mut timeout) })?;
Ok(timeout)
}
pub fn set_timeout(&self, timeout: u32) -> Result<()> {
try_d3xx!(unsafe { ffi::FT_SetPipeTimeout(self.handle, u8::from(self.id), timeout) })
}
pub async fn read_async(&self, buf: &mut [u8]) -> Result<usize> {
let mut overlapped = Overlapped::with_handle(self.handle)?;
self.maybe_abort(ffi::util::read_pipe_async(
self.handle,
u8::from(self.id),
buf,
overlapped.inner_mut(),
))?;
overlapped.await
}
pub async fn write_async(&self, buf: &[u8]) -> Result<usize> {
let mut overlapped = Overlapped::with_handle(self.handle)?;
self.maybe_abort(ffi::util::write_pipe_async(
self.handle,
u8::from(self.id),
buf,
overlapped.inner_mut(),
))?;
overlapped.await
}
}
impl<'a> Write for PipeIo<'a> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let res = ffi::util::write_pipe(self.handle, u8::from(self.id), buf);
Ok(self.maybe_abort(res)?)
}
fn flush(&mut self) -> std::io::Result<()> {
try_d3xx!(unsafe { ffi::FT_FlushPipe(self.handle, u8::from(self.id)) })?;
Ok(())
}
}
impl<'a> Read for PipeIo<'a> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let res = ffi::util::read_pipe(self.handle, u8::from(self.id), buf);
Ok(self.maybe_abort(res)?)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TryFromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum Pipe {
In0 = 0x82,
In1 = 0x83,
In2 = 0x84,
In3 = 0x85,
Out0 = 0x02,
Out1 = 0x03,
Out2 = 0x04,
Out3 = 0x05,
}
impl Pipe {
#[inline]
#[must_use]
pub fn is_in(self) -> bool {
!self.is_out()
}
#[inline]
#[must_use]
pub fn is_out(self) -> bool {
(self as u8) & 0x80 == 0
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum PipeType {
Control = 0,
Isochronous = 1,
Bulk = 2,
Interrupt = 3,
}
impl From<ffi::FT_PIPE_TYPE> for PipeType {
fn from(value: ffi::FT_PIPE_TYPE) -> Self {
match value {
ffi::FT_PIPE_TYPE::FTPipeTypeControl => Self::Control,
ffi::FT_PIPE_TYPE::FTPipeTypeIsochronous => Self::Isochronous,
ffi::FT_PIPE_TYPE::FTPipeTypeBulk => Self::Bulk,
ffi::FT_PIPE_TYPE::FTPipeTypeInterrupt => Self::Interrupt,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pipeid_try_from() {
assert_eq!(Pipe::try_from(0x82), Ok(Pipe::In0));
assert_eq!(Pipe::try_from(0x83), Ok(Pipe::In1));
assert_eq!(Pipe::try_from(0x84), Ok(Pipe::In2));
assert_eq!(Pipe::try_from(0x85), Ok(Pipe::In3));
assert_eq!(Pipe::try_from(0x02), Ok(Pipe::Out0));
assert_eq!(Pipe::try_from(0x03), Ok(Pipe::Out1));
assert_eq!(Pipe::try_from(0x04), Ok(Pipe::Out2));
assert_eq!(Pipe::try_from(0x05), Ok(Pipe::Out3));
assert!(Pipe::try_from(0x00).is_err());
assert!(Pipe::try_from(0x01).is_err());
assert!(Pipe::try_from(0x06).is_err());
assert!(Pipe::try_from(0x81).is_err());
assert!(Pipe::try_from(0x86).is_err());
assert!(Pipe::try_from(0xFF).is_err());
}
#[test]
fn pipe_is_in() {
assert!(Pipe::In0.is_in());
assert!(Pipe::In1.is_in());
assert!(Pipe::In2.is_in());
assert!(Pipe::In3.is_in());
assert!(!Pipe::Out0.is_in());
assert!(!Pipe::Out1.is_in());
assert!(!Pipe::Out2.is_in());
assert!(!Pipe::Out3.is_in());
}
#[test]
fn pipe_is_out() {
assert!(!Pipe::In0.is_out());
assert!(!Pipe::In1.is_out());
assert!(!Pipe::In2.is_out());
assert!(!Pipe::In3.is_out());
assert!(Pipe::Out0.is_out());
assert!(Pipe::Out1.is_out());
assert!(Pipe::Out2.is_out());
assert!(Pipe::Out3.is_out());
}
}