use std::any::TypeId;
use std::collections::HashMap;
use std::ffi::CString;
use std::os::raw::{c_int, c_longlong, c_uint, c_void};
use std::{mem, str};
use super::*;
use crate::ffi;
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChannelType {
Voltage = ffi::iio_chan_type_IIO_VOLTAGE,
Current = ffi::iio_chan_type_IIO_CURRENT,
Power = ffi::iio_chan_type_IIO_POWER,
Accel = ffi::iio_chan_type_IIO_ACCEL,
AnglVel = ffi::iio_chan_type_IIO_ANGL_VEL,
Magn = ffi::iio_chan_type_IIO_MAGN,
Ligtht = ffi::iio_chan_type_IIO_LIGHT,
Intensity = ffi::iio_chan_type_IIO_INTENSITY,
Proximity = ffi::iio_chan_type_IIO_PROXIMITY,
Temp = ffi::iio_chan_type_IIO_TEMP,
Incli = ffi::iio_chan_type_IIO_INCLI,
Rot = ffi::iio_chan_type_IIO_ROT,
Angl = ffi::iio_chan_type_IIO_ANGL,
Timestamp = ffi::iio_chan_type_IIO_TIMESTAMP,
Capacitance = ffi::iio_chan_type_IIO_CAPACITANCE,
AltVoltage = ffi::iio_chan_type_IIO_ALTVOLTAGE,
Cct = ffi::iio_chan_type_IIO_CCT,
Pressure = ffi::iio_chan_type_IIO_PRESSURE,
HumidityRelative = ffi::iio_chan_type_IIO_HUMIDITYRELATIVE,
Activity = ffi::iio_chan_type_IIO_ACTIVITY,
Steps = ffi::iio_chan_type_IIO_STEPS,
Energy = ffi::iio_chan_type_IIO_ENERGY,
Distance = ffi::iio_chan_type_IIO_DISTANCE,
Velocity = ffi::iio_chan_type_IIO_VELOCITY,
Concentration = ffi::iio_chan_type_IIO_CONCENTRATION,
Resistance = ffi::iio_chan_type_IIO_RESISTANCE,
Ph = ffi::iio_chan_type_IIO_PH,
UvIndex = ffi::iio_chan_type_IIO_UVINDEX,
ElectricalConductivity = ffi::iio_chan_type_IIO_ELECTRICALCONDUCTIVITY,
Count = ffi::iio_chan_type_IIO_COUNT,
Index = ffi::iio_chan_type_IIO_INDEX,
Gravity = ffi::iio_chan_type_IIO_GRAVITY,
Unknown = ffi::iio_chan_type_IIO_CHAN_TYPE_UNKNOWN,
}
#[derive(Debug, Copy, Clone)]
pub struct DataFormat {
data_fmt: ffi::iio_data_format,
}
impl DataFormat {
fn new(data_fmt: ffi::iio_data_format) -> Self {
DataFormat { data_fmt }
}
pub fn length(&self) -> u32 {
u32::from(self.data_fmt.length)
}
pub fn bits(&self) -> u32 {
u32::from(self.data_fmt.bits)
}
pub fn shift(&self) -> u32 {
u32::from(self.data_fmt.shift)
}
pub fn is_signed(&self) -> bool {
self.data_fmt.is_signed
}
pub fn is_fully_defined(&self) -> bool {
self.data_fmt.is_fully_defined
}
pub fn is_big_endian(&self) -> bool {
self.data_fmt.is_be
}
pub fn with_scale(&self) -> bool {
self.data_fmt.with_scale
}
pub fn scale(&self) -> f64 {
self.data_fmt.scale
}
pub fn repeat(&self) -> u32 {
u32::from(self.data_fmt.repeat)
}
pub fn byte_length(&self) -> usize {
let nbytes = (self.length() / 8) * self.repeat();
nbytes as usize
}
pub fn type_of(&self) -> Option<TypeId> {
let nbytes = self.byte_length();
if self.is_signed() {
match nbytes {
1 => Some(TypeId::of::<i8>()),
2 => Some(TypeId::of::<i16>()),
4 => Some(TypeId::of::<i32>()),
8 => Some(TypeId::of::<i64>()),
_ => None,
}
}
else {
match nbytes {
1 => Some(TypeId::of::<u8>()),
2 => Some(TypeId::of::<u16>()),
4 => Some(TypeId::of::<u32>()),
8 => Some(TypeId::of::<u64>()),
_ => None,
}
}
}
}
pub struct Channel {
pub(crate) chan: *mut ffi::iio_channel,
#[allow(dead_code)]
pub(crate) ctx: Context,
}
impl Channel {
pub fn name(&self) -> Option<String> {
let pstr = unsafe { ffi::iio_channel_get_name(self.chan) };
cstring_opt(pstr)
}
pub fn id(&self) -> Option<String> {
let pstr = unsafe { ffi::iio_channel_get_id(self.chan) };
cstring_opt(pstr)
}
pub fn is_output(&self) -> bool {
unsafe { ffi::iio_channel_is_output(self.chan) }
}
pub fn is_scan_element(&self) -> bool {
unsafe { ffi::iio_channel_is_scan_element(self.chan) }
}
pub fn index(&self) -> Result<usize> {
let ret = unsafe { ffi::iio_channel_get_index(self.chan) };
sys_result(ret as i32, ret as usize)
}
pub fn num_attrs(&self) -> usize {
let n = unsafe { ffi::iio_channel_get_attrs_count(self.chan) };
n as usize
}
pub fn has_attr(&self, attr: &str) -> bool {
let attr = cstring_or_bail_false!(attr);
unsafe { !ffi::iio_channel_find_attr(self.chan, attr.as_ptr()).is_null() }
}
pub fn get_attr(&self, idx: usize) -> Result<String> {
let pstr = unsafe { ffi::iio_channel_get_attr(self.chan, idx as c_uint) };
cstring_opt(pstr).ok_or_else(|| Error::InvalidIndex)
}
pub fn find_attr(&self, name: &str) -> Option<String> {
let cname = cstring_or_bail!(name);
let pstr = unsafe { ffi::iio_channel_find_attr(self.chan, cname.as_ptr()) };
cstring_opt(pstr)
}
pub fn attr_read_bool(&self, attr: &str) -> Result<bool> {
let mut val: bool = false;
let attr = CString::new(attr)?;
let ret = unsafe { ffi::iio_channel_attr_read_bool(self.chan, attr.as_ptr(), &mut val) };
sys_result(ret, val)
}
pub fn attr_read_int(&self, attr: &str) -> Result<i64> {
let mut val: c_longlong = 0;
let attr = CString::new(attr)?;
let ret =
unsafe { ffi::iio_channel_attr_read_longlong(self.chan, attr.as_ptr(), &mut val) };
sys_result(ret, val as i64)
}
pub fn attr_read_float(&self, attr: &str) -> Result<f64> {
let mut val: f64 = 0.0;
let attr = CString::new(attr)?;
let ret = unsafe { ffi::iio_channel_attr_read_double(self.chan, attr.as_ptr(), &mut val) };
sys_result(ret, val)
}
unsafe extern "C" fn attr_read_all_cb(
_chan: *mut ffi::iio_channel,
attr: *const c_char,
val: *const c_char,
_len: usize,
pmap: *mut c_void,
) -> c_int {
if attr.is_null() || val.is_null() || pmap.is_null() {
return -1;
}
let attr = CStr::from_ptr(attr).to_string_lossy().to_string();
let val = CStr::from_ptr(val).to_string_lossy().to_string();
let map: &mut HashMap<String, String> = &mut *(pmap as *mut _);
map.insert(attr, val);
0
}
pub fn attr_read_all(&self) -> Result<HashMap<String, String>> {
let mut map = HashMap::new();
let pmap = &mut map as *mut _ as *mut c_void;
let ret = unsafe {
ffi::iio_channel_attr_read_all(self.chan, Some(Channel::attr_read_all_cb), pmap)
};
sys_result(ret, map)
}
pub fn attr_write_bool(&self, attr: &str, val: bool) -> Result<()> {
let attr = CString::new(attr)?;
let ret = unsafe { ffi::iio_channel_attr_write_bool(self.chan, attr.as_ptr(), val) };
sys_result(ret, ())
}
pub fn attr_write_int(&self, attr: &str, val: i64) -> Result<()> {
let attr = CString::new(attr)?;
let ret = unsafe { ffi::iio_channel_attr_write_longlong(self.chan, attr.as_ptr(), val) };
sys_result(ret, ())
}
pub fn attr_write_float(&self, attr: &str, val: f64) -> Result<()> {
let attr = CString::new(attr)?;
let ret = unsafe { ffi::iio_channel_attr_write_double(self.chan, attr.as_ptr(), val) };
sys_result(ret, ())
}
pub fn attrs(&self) -> AttrIterator {
AttrIterator { chan: self, idx: 0 }
}
pub fn enable(&mut self) {
unsafe { ffi::iio_channel_enable(self.chan) };
}
pub fn disable(&mut self) {
unsafe { ffi::iio_channel_disable(self.chan) };
}
pub fn is_enabled(&self) -> bool {
unsafe { ffi::iio_channel_is_enabled(self.chan) }
}
pub fn data_format(&self) -> DataFormat {
unsafe {
let pfmt = ffi::iio_channel_get_data_format(self.chan);
DataFormat::new(*pfmt)
}
}
pub fn type_of(&self) -> Option<TypeId> {
let dfmt = self.data_format();
dfmt.type_of()
}
pub fn channel_type(&self) -> ChannelType {
unsafe {
let n = ffi::iio_channel_get_type(self.chan);
mem::transmute(n)
}
}
pub fn convert<T>(&self, val: T) -> T
where
T: Copy + 'static,
{
let mut retval = val;
if self.type_of() == Some(TypeId::of::<T>()) {
unsafe {
ffi::iio_channel_convert(
self.chan,
&mut retval as *mut T as *mut c_void,
&val as *const T as *const c_void,
);
}
}
retval
}
pub fn convert_inverse<T>(&self, val: T) -> T
where
T: Copy + 'static,
{
let mut retval = val;
if self.type_of() == Some(TypeId::of::<T>()) {
unsafe {
ffi::iio_channel_convert_inverse(
self.chan,
&mut retval as *mut T as *mut c_void,
&val as *const T as *const c_void,
);
}
}
retval
}
pub fn read<T>(&self, buf: &Buffer) -> Result<Vec<T>>
where
T: Default + Copy + 'static,
{
if self.type_of() != Some(TypeId::of::<T>()) {
return Err(Error::WrongDataType);
}
let n = buf.capacity();
let sz_item = mem::size_of::<T>();
let sz_in = n * sz_item;
let mut v = vec![T::default(); n];
let sz = unsafe {
ffi::iio_channel_read(self.chan, buf.buf, v.as_mut_ptr() as *mut c_void, sz_in)
};
if sz > sz_in {
return Err(Error::BadReturnSize); }
if sz < sz_in {
v.truncate(sz / sz_item);
}
Ok(v)
}
pub fn read_raw<T>(&self, buf: &Buffer) -> Result<Vec<T>>
where
T: Default + Copy + 'static,
{
if self.type_of() != Some(TypeId::of::<T>()) {
return Err(Error::WrongDataType);
}
let n = buf.capacity();
let sz_item = mem::size_of::<T>();
let sz_in = n * sz_item;
let mut v = vec![T::default(); n];
let sz = unsafe {
ffi::iio_channel_read_raw(self.chan, buf.buf, v.as_mut_ptr() as *mut c_void, sz_in)
};
if sz > sz_in {
return Err(Error::BadReturnSize); }
if sz < sz_in {
v.truncate(sz / sz_item);
}
Ok(v)
}
pub fn write<T>(&self, buf: &Buffer, data: &[T]) -> Result<usize>
where
T: Default + Copy + 'static,
{
if self.type_of() != Some(TypeId::of::<T>()) {
return Err(Error::WrongDataType);
}
let sz_item = mem::size_of::<T>();
let sz_in = data.len() * sz_item;
let sz = unsafe {
ffi::iio_channel_write(self.chan, buf.buf, data.as_ptr() as *const c_void, sz_in)
};
Ok(sz / sz_item)
}
pub fn write_raw<T>(&self, buf: &Buffer, data: &[T]) -> Result<usize>
where
T: Default + Copy + 'static,
{
if self.type_of() != Some(TypeId::of::<T>()) {
return Err(Error::WrongDataType);
}
let sz_item = mem::size_of::<T>();
let sz_in = data.len() * sz_item;
let sz = unsafe {
ffi::iio_channel_write(self.chan, buf.buf, data.as_ptr() as *const c_void, sz_in)
};
Ok(sz / sz_item)
}
}
pub struct AttrIterator<'a> {
chan: &'a Channel,
idx: usize,
}
impl<'a> Iterator for AttrIterator<'a> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
match self.chan.get_attr(self.idx) {
Ok(name) => {
self.idx += 1;
Some(name)
}
Err(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_context() {
let dev = Context::new().unwrap().get_device(0).unwrap();
let chan = dev.get_channel(0);
assert!(chan.is_ok());
}
}