Commit 96609a19 authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

samples: rust: add Rust auxiliary driver sample



Add a sample Rust auxiliary driver based on a PCI driver for QEMU's
"pci-testdev" device.

The PCI driver only registers an auxiliary device, in order to make the
corresponding auxiliary driver probe.

Acked-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250414131934.28418-6-dakr@kernel.org


[ Use `ok_or()` when accessing auxiliary::Device::parent(). - Danilo ]
Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 0d1803d2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3874,6 +3874,7 @@ F: drivers/base/auxiliary.c
F:	include/linux/auxiliary_bus.h
F:	rust/helpers/auxiliary.c
F:	rust/kernel/auxiliary.rs
F:	samples/rust/rust_driver_auxiliary.rs
AUXILIARY DISPLAY DRIVERS
M:	Andy Shevchenko <andy@kernel.org>
+12 −0
Original line number Diff line number Diff line
@@ -82,6 +82,18 @@ config SAMPLE_RUST_DRIVER_FAUX

	  If unsure, say N.

config SAMPLE_RUST_DRIVER_AUXILIARY
	tristate "Auxiliary Driver"
	depends on AUXILIARY_BUS
	depends on PCI
	help
	  This option builds the Rust auxiliary driver sample.

	  To compile this as a module, choose M here:
	  the module will be called rust_driver_auxiliary.

	  If unsure, say N.

config SAMPLE_RUST_HOSTPROGS
	bool "Host programs"
	help
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o

rust_print-y := rust_print_main.o rust_print_events.o

+120 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

//! Rust auxiliary driver sample (based on a PCI driver for QEMU's `pci-testdev`).
//!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.

use kernel::{
    auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr,
    InPlaceModule,
};

use pin_init::PinInit;

const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
const AUXILIARY_NAME: &CStr = c_str!("auxiliary");

struct AuxiliaryDriver;

kernel::auxiliary_device_table!(
    AUX_TABLE,
    MODULE_AUX_TABLE,
    <AuxiliaryDriver as auxiliary::Driver>::IdInfo,
    [(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())]
);

impl auxiliary::Driver for AuxiliaryDriver {
    type IdInfo = ();

    const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;

    fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
        dev_info!(
            adev.as_ref(),
            "Probing auxiliary driver for auxiliary device with id={}\n",
            adev.id()
        );

        ParentDriver::connect(adev)?;

        let this = KBox::new(Self, GFP_KERNEL)?;

        Ok(this.into())
    }
}

struct ParentDriver {
    _reg: [auxiliary::Registration; 2],
}

kernel::pci_device_table!(
    PCI_TABLE,
    MODULE_PCI_TABLE,
    <ParentDriver as pci::Driver>::IdInfo,
    [(
        pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
        ()
    )]
);

impl pci::Driver for ParentDriver {
    type IdInfo = ();

    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;

    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
        let this = KBox::new(
            Self {
                _reg: [
                    auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?,
                    auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?,
                ],
            },
            GFP_KERNEL,
        )?;

        Ok(this.into())
    }
}

impl ParentDriver {
    fn connect(adev: &auxiliary::Device) -> Result<()> {
        let parent = adev.parent().ok_or(EINVAL)?;
        let pdev: &pci::Device = parent.try_into()?;

        dev_info!(
            adev.as_ref(),
            "Connect auxiliary {} with parent: VendorID={:#x}, DeviceID={:#x}\n",
            adev.id(),
            pdev.vendor_id(),
            pdev.device_id()
        );

        Ok(())
    }
}

#[pin_data]
struct SampleModule {
    #[pin]
    _pci_driver: driver::Registration<pci::Adapter<ParentDriver>>,
    #[pin]
    _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
}

impl InPlaceModule for SampleModule {
    fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
        try_pin_init!(Self {
            _pci_driver <- driver::Registration::new(MODULE_NAME, module),
            _aux_driver <- driver::Registration::new(MODULE_NAME, module),
        })
    }
}

module! {
    type: SampleModule,
    name: "rust_driver_auxiliary",
    author: "Danilo Krummrich",
    description: "Rust auxiliary driver",
    license: "GPL v2",
}