use std::{
ffi::{c_void, CString},
fmt::Debug,
marker::PhantomData,
mem::ManuallyDrop,
ptr::addr_of_mut,
};
use crate::{
descriptor::{ConfigurationDescriptor, DeviceDescriptor, InterfaceDescriptor},
ffi::{self, with_global_lock},
gpio::{Gpio, GpioPin},
notification::{clear_notification_callback, set_notification_callback, Notification},
try_d3xx,
util::PhantomUnsync,
Pipe, PipeIo, Result, Version,
};
#[derive(Debug)]
pub struct Device {
handle: ffi::FT_HANDLE,
_unsync: PhantomUnsync,
}
impl Device {
pub fn open(serial_number: &str) -> Result<Self> {
let serial_cstr =
CString::new(serial_number).expect("serial number is not a valid C string");
let handle = with_global_lock(|| {
let mut handle: ffi::FT_HANDLE = std::ptr::null_mut();
try_d3xx!(unsafe {
ffi::FT_Create(
serial_cstr.as_ptr() as *mut c_void,
ffi::FT_OPEN_BY_SERIAL_NUMBER,
&mut handle,
)
})
.and(Ok(handle))
})?;
if handle.is_null() {
Err(crate::D3xxError::DeviceNotFound)
} else {
Ok(unsafe { Self::with_handle(handle) })
}
}
pub unsafe fn with_handle(handle: ffi::FT_HANDLE) -> Self {
Self {
handle,
_unsync: PhantomData,
}
}
#[must_use]
pub fn handle(&self) -> ffi::FT_HANDLE {
self.handle
}
#[must_use]
pub fn into_handle(self) -> ffi::FT_HANDLE {
let device = ManuallyDrop::new(self);
device.handle
}
pub fn device_descriptor(&self) -> Result<DeviceDescriptor> {
DeviceDescriptor::new(self.handle)
}
pub fn configuration_descriptor(&self) -> Result<ConfigurationDescriptor> {
ConfigurationDescriptor::new(self.handle)
}
pub fn interface_descriptor(&self, interface: u8) -> Result<InterfaceDescriptor> {
InterfaceDescriptor::new(self.handle, interface)
}
#[cfg(feature = "config")]
pub fn chip_configuration(&self) -> Result<crate::configuration::ChipConfiguration> {
crate::configuration::ChipConfiguration::new(self.handle)
}
#[must_use]
pub fn pipe(&self, id: Pipe) -> PipeIo {
PipeIo::new(self, id)
}
#[must_use]
pub fn gpio(&self, pin: GpioPin) -> Gpio {
Gpio::new(self, pin)
}
pub fn driver_version(&self) -> Result<Version> {
let mut version: u32 = 0;
try_d3xx!(unsafe { ffi::FT_GetDriverVersion(self.handle, addr_of_mut!(version)) })?;
Ok(Version(version))
}
pub fn power_cycle_port(self) -> Result<()> {
let handle = self.into_handle();
try_d3xx!(unsafe { ffi::FT_CycleDevicePort(handle) })?;
Ok(())
}
#[cfg(windows)]
pub fn suspend_timeout(&self) -> Result<u32> {
let mut timeout: u32 = 0;
try_d3xx!(unsafe { ffi::FT_GetSuspendTimeout(self.handle, addr_of_mut!(timeout)) })?;
Ok(timeout)
}
#[cfg(windows)]
pub fn set_suspend_timeout(&self, timeout: Option<u32>) -> Result<()> {
let timeout = timeout.unwrap_or(0);
try_d3xx!(unsafe { ffi::FT_SetSuspendTimeout(self.handle, timeout) })?;
Ok(())
}
pub fn set_notification_callback<F, T>(&self, callback: F, context: Option<T>) -> Result<()>
where
T: Sync,
F: Fn(Notification<T>) + 'static,
{
set_notification_callback(self.handle, callback, context)
}
pub fn clear_notification_callback(&self) {
unsafe {
clear_notification_callback(self.handle);
}
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
let _ = ffi::FT_Close(self.handle);
}
}
}
unsafe impl Send for Device {}