Implementing drivers via with some common functionality

I'm currently looking on how to improve existing phy drivers for lora-rs project.

LoRa phy layer expects each driver to implement so called RadioKind trait which contains bunch of calls to configure driver (ie configure modem (set power, frequency, modulation, then prepare IRQ, and switch radio to either transmit/receive mode).

I was hoping that there's a possibility to have a top-level: sx127x driver, implementing the RadioKind trait, and deals with most common tasks by itself, but in certain cases where sx1272 and sx1276 (or sx1277, sx1278 or sx1279) differ, the call will be dispatched to driver-specific implementation.
I initially tried to implement the sealed trait patteren, but I don't feel this is the way to go.

My idea was kind of following or is the correct approach instead implementing RadioKind for each chip separately?

pub mod ic {
    pub struct Sx1272(());
    pub struct Sx1276(());
}

pub struct Sx127x<SPI, IV, IC> {
    intf: SpiInterface<SPI, IV>,
    _ic: PhantomData<IC>,

   // Methods for common SPI functionality (read register, write register, read buf, write buf)
}

impl<SPI, IV, IC> RadioKind for Sx127x<SPI, IV, IC>                                            
where
    SPI: SpiDevice<u8>,
    IV: InterfaceVariant,
{
  // Implement all methods required for RadioKind
      fn create_modulation_params(
        &self,
        spreading_factor: SpreadingFactor,
        bandwidth: Bandwidth,
        coding_rate: CodingRate,
        frequency_in_hz: u32,
    ) -> Result<ModulationParams, RadioError> {
        // TODO: How to best call "driver-specific implementation" as each chip variant has different frequency range
       validate_bandwidth(bandwidth:: Bandwidth)?;
        Ok(ModulationParams {
            spreading_factor,
            bandwidth,
            coding_rate,
            low_data_rate_optimize: 0,
            frequency_in_hz,
        })
    }

    async fn set_tx_power_and_ramp_time(&mut self,
        output_power: i32,
        mdltn_params: Option<&ModulationParams>,
        tx_boosted_if_possible: bool,
        is_tx_prep: bool,
    ) -> Result<(), RadioError> {
       // TODO: chip.set_power(...)
    }
}

impl<SPI, IV> Sx127x<SPI, IV, ic::Sx1272>
where
    SPI: SpiDevice<u8>,
    IV: InterfaceVariant,
{
    pub fn new_sx1272(spi: SPI, iv: IV) -> Self {
        Sx127x {
            intf: SpiInterface::new(spi, iv),
            _ic: PhantomData,
        }
    }
    // TODO? Implementing chip-specific configuration state and methods?
}

If I understand your problem correctly, it's that it's too much work / too difficult to write the complete implementation of the RadioKind trait.

If so, what you could do is provide a type DefaultRadioKindImpl: RadioKind.
If the driver writer then only wants that functionality, they have it right there.
On the other hand, if they want to modify the impl, what they can then do is write a wrapper type WrappedRadioKindImpl(DefaultRadioKindImpl): RadioKind that just delegates its trait impl to its internal field for most of the stuff, and just deviates where appropriate.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.