rdif-pinctrl 0.1.2

Driver Interface pin control definition.
Documentation
#![no_std]

extern crate alloc;

mod error;
#[cfg(feature = "fdt")]
mod fdt;
mod gpio;
mod id;
mod interface;
mod irq;
mod types;

pub use error::*;
#[cfg(feature = "fdt")]
pub use fdt::*;
pub use gpio::*;
pub use id::*;
pub use interface::*;
pub use irq::*;
pub use rdif_base::{DriverGeneric, KError, io};
pub use types::*;

#[cfg(test)]
mod tests {
    use alloc::{boxed::Box, string::String, vec, vec::Vec};

    use super::*;

    struct Recorder {
        calls: Vec<&'static str>,
        pins: Vec<PinDesc>,
        groups: Vec<PinGroup>,
        functions: Vec<PinFunction>,
    }

    impl Recorder {
        fn new() -> Self {
            Self {
                calls: Vec::new(),
                pins: vec![PinDesc::new(PinId::new(1), Some("gpio1"))],
                groups: vec![PinGroup::new(
                    GroupId::new(1),
                    Some("uart0"),
                    vec![PinId::new(1)],
                )],
                functions: vec![PinFunction::new(
                    FunctionId::new(1),
                    Some("uart"),
                    vec![GroupId::new(1)],
                )],
            }
        }
    }

    impl DriverGeneric for Recorder {
        fn name(&self) -> &str {
            "recorder"
        }
    }

    impl Interface for Recorder {
        fn pins(&self) -> &[PinDesc] {
            &self.pins
        }

        fn groups(&self) -> &[PinGroup] {
            &self.groups
        }

        fn functions(&self) -> &[PinFunction] {
            &self.functions
        }

        fn apply_mux(&mut self, _setting: &MuxSetting) -> Result<(), PinctrlError> {
            self.calls.push("mux");
            Ok(())
        }

        fn apply_config(&mut self, _setting: &ConfigSetting) -> Result<(), PinctrlError> {
            self.calls.push("config");
            Ok(())
        }
    }

    #[test]
    fn apply_state_programs_mux_before_config() {
        let mut recorder = Recorder::new();
        let state = PinState::named(StateName::Default)
            .with_mux(MuxSetting::new(
                GroupId::new(1),
                FunctionId::new(1),
                MuxValue::new(8),
            ))
            .with_config(ConfigSetting::pin(
                PinId::new(1),
                PinConfig::DriveStrengthUa(8000),
            ));

        recorder.apply_state(&state).unwrap();

        assert_eq!(recorder.calls, vec!["mux", "config"]);
    }

    #[test]
    fn validate_state_rejects_invalid_group_function_pair() {
        let recorder = Recorder::new();
        let state = PinState::named(StateName::Default).with_mux(MuxSetting::new(
            GroupId::new(1),
            FunctionId::new(99),
            MuxValue::new(2),
        ));

        assert_eq!(
            recorder.validate_state(&state),
            Err(PinctrlError::InvalidFunction(FunctionId::new(99)))
        );
    }

    struct MockBank {
        requested: Option<GpioLineHandle>,
        value: bool,
    }

    impl MockBank {
        fn new() -> Self {
            Self {
                requested: None,
                value: false,
            }
        }

        fn ensure_requested(&self, handle: &GpioLineHandle) -> Result<(), PinctrlError> {
            if self.requested.as_ref() == Some(handle) {
                Ok(())
            } else {
                Err(PinctrlError::LineNotRequested(handle.line()))
            }
        }
    }

    impl GpioBank for MockBank {
        fn bank_id(&self) -> GpioBankId {
            GpioBankId::new(0)
        }

        fn line_count(&self) -> u32 {
            8
        }

        fn request_line(
            &mut self,
            line: GpioLineId,
            owner: &str,
        ) -> Result<GpioLineHandle, PinctrlError> {
            assert_eq!(owner, "uart");
            let handle = GpioLineHandle::new(line, OwnerId::new(7));
            self.requested = Some(handle);
            Ok(handle)
        }

        fn release_line(&mut self, handle: GpioLineHandle) -> Result<(), PinctrlError> {
            self.ensure_requested(&handle)?;
            self.requested = None;
            Ok(())
        }

        fn set_direction(
            &mut self,
            handle: &GpioLineHandle,
            _direction: Direction,
        ) -> Result<(), PinctrlError> {
            self.ensure_requested(handle)
        }

        fn read(&self, handle: &GpioLineHandle) -> Result<bool, PinctrlError> {
            self.ensure_requested(handle)?;
            Ok(self.value)
        }

        fn write(&mut self, handle: &GpioLineHandle, value: bool) -> Result<(), PinctrlError> {
            self.ensure_requested(handle)?;
            self.value = value;
            Ok(())
        }
    }

    #[test]
    fn gpio_line_handle_is_required_for_line_io() {
        let mut bank = MockBank::new();
        let line = GpioLineId::new(GpioBankId::new(0), 3);
        let forged = GpioLineHandle::new(line, OwnerId::new(9));

        assert_eq!(
            bank.write(&forged, true),
            Err(PinctrlError::LineNotRequested(line))
        );

        let handle = bank.request_line(line, "uart").unwrap();
        bank.set_direction(&handle, Direction::Output { initial: false })
            .unwrap();
        bank.write(&handle, true).unwrap();
        assert!(bank.read(&handle).unwrap());
        bank.release_line(handle).unwrap();
    }

    struct MockIrq {
        line: GpioLineId,
    }

    impl GpioIrqHandler for MockIrq {
        fn handle_irq(&mut self) -> GpioIrqEvent {
            GpioIrqEvent::from_line(GpioLineEvent::new(self.line, GpioIrqTrigger::EdgeRising))
        }
    }

    #[test]
    fn gpio_irq_handler_reports_stable_event_without_os_types() {
        let mut irq = MockIrq {
            line: GpioLineId::new(GpioBankId::new(2), 5),
        };

        let event = irq.handle_irq();

        assert_eq!(event.lines().len(), 1);
        assert_eq!(
            event.lines()[0].line,
            GpioLineId::new(GpioBankId::new(2), 5)
        );
        assert_eq!(event.lines()[0].trigger, GpioIrqTrigger::EdgeRising);
    }

    #[test]
    fn acpi_firmware_specs_can_represent_explicit_unsupported_mapping() {
        let spec = AcpiPinStateSpec::new(String::from("\\_SB.GPIO"), StateName::Default);

        assert_eq!(spec.state_name(), &StateName::Default);
        assert_eq!(
            PinctrlError::UnsupportedFirmware(FirmwareKind::Acpi),
            PinctrlError::UnsupportedFirmware(FirmwareKind::Acpi)
        );
    }

    #[test]
    fn interface_can_transfer_gpio_irq_handler_ownership() {
        struct Device {
            handler: Option<Box<dyn GpioIrqHandler>>,
        }

        impl DriverGeneric for Device {
            fn name(&self) -> &str {
                "dev"
            }
        }

        impl Interface for Device {
            fn take_irq_handler(
                &mut self,
                source_id: GpioIrqSourceId,
            ) -> Option<Box<dyn GpioIrqHandler>> {
                if source_id == GpioIrqSourceId::new(0) {
                    self.handler.take()
                } else {
                    None
                }
            }
        }

        let mut device = Device {
            handler: Some(Box::new(MockIrq {
                line: GpioLineId::new(GpioBankId::new(0), 1),
            })),
        };

        assert!(device.take_irq_handler(GpioIrqSourceId::new(0)).is_some());
        assert!(device.take_irq_handler(GpioIrqSourceId::new(0)).is_none());
    }

    #[test]
    fn pinctrl_device_wraps_interface_as_queryable_capability() {
        let mut device = PinctrlDevice::new(Recorder::new());
        let state = PinState::named(StateName::Default).with_mux(MuxSetting::new(
            GroupId::new(1),
            FunctionId::new(1),
            MuxValue::new(8),
        ));

        device.apply_state(&state).unwrap();

        assert_eq!(device.name(), "recorder");
        assert!(device.typed_ref::<Recorder>().is_some());
        assert_eq!(device.typed_mut::<Recorder>().unwrap().calls, vec!["mux"]);
    }
}