emc230x 0.3.0

An async driver for the EMC230x family of fan controllers
Documentation
use ector::{Actor, ActorRequest, Address, DynamicAddress, Request};
use embassy_sync::{
    blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex},
    mutex::Mutex,
};
use embedded_hal_async as hal;
use hal::i2c::I2c;
use heapless::Vec;

use crate::{Emc230x, Error, FanSelect};

type Transaction = Request<Message, Result<Response, Error>>;

#[derive(Clone, Copy, Debug)]
pub struct DeviceSelect(pub u8);

#[derive(Clone, Copy, Debug)]
pub enum Message {
    GetDeviceCount,
    GetDutyCycle(DeviceSelect, FanSelect),
    SetDutyCycle(DeviceSelect, FanSelect, u8),
    GetFanCount(DeviceSelect),
    GetSpeed(DeviceSelect, FanSelect),
    SetSpeed(DeviceSelect, FanSelect, u16),
}

pub enum Response {
    Ack,
    DeviceCount(u8),
    DutyCycle(u8),
    FanCount(u8),
    Speed(u16),
}

/// A service that interfaces with multiple EMC230x devices.
///
/// This is useful for decoupling platform specifics from interacting with the EMC230x devices. This
/// may occur if a platform has a dynamic number of devices configured.
///
/// N: The number of devices that the service can support.
pub struct Service<I2C, const N: usize> {
    dev_list: Vec<Emc230x<I2C>, N>,
}

impl<I2C: I2c, const N: usize> Service<I2C, N> {
    pub fn new(devs: Vec<Emc230x<I2C>, N>) -> Self {
        Self { dev_list: devs }
    }
}

impl<I2C: I2c, const N: usize> Actor for Service<I2C, N> {
    type Message = Transaction;

    async fn on_mount<M>(&mut self, _: DynamicAddress<Self::Message>, mut inbox: M) -> !
    where
        M: ector::Inbox<Self::Message>,
    {
        loop {
            let request = inbox.next().await;
            let msg = request.as_ref();
            log::info!("Received message: {:?}", msg);
            let reply = match msg {
                Message::GetDeviceCount => Ok(Response::DeviceCount(self.dev_list.len() as u8)),
                Message::SetDutyCycle(dev, fan, duty) => {
                    if let Some(dev) = self.dev_list.get_mut(dev.0 as usize) {
                        match dev.set_duty_cycle(*fan, *duty).await {
                            Ok(_) => Ok(Response::Ack),
                            Err(e) => Err(e),
                        }
                    } else {
                        Err(Error::InvalidDeviceSelection)
                    }
                }
                Message::GetDutyCycle(dev, fan) => {
                    if let Some(dev) = self.dev_list.get_mut(dev.0 as usize) {
                        match dev.duty_cycle(*fan).await {
                            Ok(duty) => Ok(Response::DutyCycle(duty)),
                            Err(e) => Err(e),
                        }
                    } else {
                        Err(Error::InvalidDeviceSelection)
                    }
                }
                Message::GetFanCount(dev) => {
                    if let Some(dev) = self.dev_list.get_mut(dev.0 as usize) {
                        Ok(Response::FanCount(dev.count()))
                    } else {
                        Err(Error::InvalidDeviceSelection)
                    }
                }
                Message::GetSpeed(dev, fan) => {
                    if let Some(dev) = self.dev_list.get_mut(dev.0 as usize) {
                        match dev.rpm(*fan).await {
                            Ok(speed) => Ok(Response::Speed(speed)),
                            Err(e) => Err(e),
                        }
                    } else {
                        Err(Error::InvalidDeviceSelection)
                    }
                }
                Message::SetSpeed(dev, fan, speed) => {
                    if let Some(dev) = self.dev_list.get_mut(dev.0 as usize) {
                        match dev.set_rpm(*fan, *speed).await {
                            Ok(_) => Ok(Response::Ack),
                            Err(e) => Err(e),
                        }
                    } else {
                        Err(Error::InvalidDeviceSelection)
                    }
                }
            };
            request.reply(reply).await;
        }
    }
}

//embassy_sync::channel::Sender<'_, CriticalSectionRawMutex, PlatformEmc230xServiceMessage, 1>
use embassy_sync::channel::Sender;

trait Fan {
    async fn set_duty_cycle(&self, duty: u8) -> Result<(), Error>;
    async fn duty_cycle(&self) -> Result<u8, Error>;
    async fn set_speed(&self, speed: u16) -> Result<(), Error>;
    async fn speed(&self) -> Result<u16, Error>;
}

pub struct Emc230xFan<M: ector::mutex::RawMutex + 'static, const N: usize> {
    dev: DeviceSelect,
    fan: FanSelect,
    tx: Address<Transaction, M, N>,
}

impl<M: ector::mutex::RawMutex, const N: usize> Emc230xFan<M, N> {
    /// Create a new list of fans attached to the EMC230x service.
    pub async fn new_list<const V: usize>(
        addr: Address<Transaction, M, N>,
    ) -> Result<Vec<Self, V>, Error> {
        let dev_count = match addr.request(Message::GetDeviceCount).await? {
            Response::DeviceCount(c) => c,
            _ => return Err(Error::UnexpectedServiceMessageType),
        };

        // todo: check fan count against vector capacity

        let mut fans = Vec::new();
        for dev in 0..dev_count {
            let fan_count = match addr
                .request(Message::GetFanCount(DeviceSelect(dev)))
                .await?
            {
                Response::FanCount(c) => c,
                _ => return Err(Error::UnexpectedServiceMessageType),
            };

            for fan in 1..=fan_count {
                let res = fans.push(Self {
                    dev: DeviceSelect(dev),
                    fan: FanSelect(fan),
                    tx: addr.clone(),
                });

                if res.is_err() {
                    return Err(Error::ServiceDeviceVectorFull);
                }
            }
        }

        Ok(fans)
    }
}

impl<M: ector::mutex::RawMutex, const N: usize> Fan for Emc230xFan<M, N> {
    async fn set_duty_cycle(&self, duty: u8) -> Result<(), Error> {
        match self
            .tx
            .request(Message::SetDutyCycle(self.dev, self.fan, duty))
            .await
        {
            Ok(Response::Ack) => Ok(()),
            Err(e) => Err(e),
            _ => Err(Error::UnexpectedServiceMessageType),
        }
    }

    async fn duty_cycle(&self) -> Result<u8, Error> {
        match self
            .tx
            .request(Message::GetDutyCycle(self.dev, self.fan))
            .await
        {
            Ok(Response::DutyCycle(duty)) => Ok(duty),
            Err(e) => Err(e),
            _ => Err(Error::UnexpectedServiceMessageType),
        }
    }

    async fn set_speed(&self, speed: u16) -> Result<(), Error> {
        match self
            .tx
            .request(Message::SetSpeed(self.dev, self.fan, speed))
            .await
        {
            Ok(Response::Ack) => Ok(()),
            Err(e) => Err(e),
            _ => Err(Error::UnexpectedServiceMessageType),
        }
    }

    async fn speed(&self) -> Result<u16, Error> {
        match self.tx.request(Message::GetSpeed(self.dev, self.fan)).await {
            Ok(Response::Speed(s)) => Ok(s),
            Err(e) => Err(e),
            _ => Err(Error::UnexpectedServiceMessageType),
        }
    }
}