Driver core changes for 6.19-rc1

- Arch Topology:
   - Move parse_acpi_topology() from arm64 to common code for reuse in RISC-V
 
 - CPU:
   - Expose housekeeping CPUs through /sys/devices/system/cpu/housekeeping
   - Print a newline (or 0x0A) instead of '(null)' reading
     /sys/devices/system/cpu/nohz_full when nohz_full= is not set
 
 - debugfs
   - Remove (broken) 'no-mount' mode
   - Remove redundant access mode checks in debugfs_get_tree() and
     debugfs_create_*() functions
 
 - Devres:
   - Remove unused devm_free_percpu() helper
   - Move devm_alloc_percpu() from device.h to devres.h
 
 - Firmware Loader:
   - Replace simple_strtol() with kstrtoint()
   - Do not call cancel_store() when no upload is in progress
 
 - kernfs:
   - Increase struct super_block::maxbytes to MAX_LFS_FILESIZE
   - Fix a missing unwind path in __kernfs_new_node()
 
 - Misc:
   - Increase the name size in struct auxiliary_device_id to 40 characters
   - Replace system_unbound_wq with system_dfl_wq and add WQ_PERCPU to
     alloc_workqueue()
 
 - Platform:
   - Replace ERR_PTR() with IOMEM_ERR_PTR() in platform ioremap functions
 
 - Rust:
   - Auxiliary:
     - Unregister auxiliary device on parent device unbind
     - Move parent() to impl Device; implement device context aware parent() for
       Device<Bound>
     - Illustrate how to safely obtain a driver's device private data when
       calling from an auxiliary driver into the parant device driver
 
   - DebugFs:
     - Implement support for binary large objects
 
   - Device:
     - Let probe() return the driver's device private data as pinned initializer,
       i.e. impl PinInit<Self, Error>
     - Implement safe accessor for a driver's device private data for
       Device<Bound> (returned reference can't out-live driver binding and
       guarantees the correct private data type)
     - Implement AsBusDevice trait, to be used by class device abstractions to
       derive the bus device type of the parent device
 
   - DMA:
     - Store raw pointer of allocation as NonNull
     - Use start_ptr() and start_ptr_mut() to inherit correct mutability of self
 
   - FS:
     - Add file::Offset type alias
 
   - I2C:
     - Add abstractions for I2C device / driver infrastructure
     - Implement abstractions for manual I2C device registrations
 
   - I/O:
     - Use "kernel vertical" style for imports
     - Define ResourceSize as resource_size_t
     - Move ResourceSize to top-level I/O module
     - Add type alias for phys_addr_t
     - Implement Rust version of read_poll_timeout_atomic()
 
   - PCI:
     - Use "kernel vertical" style for imports
     - Move I/O and IRQ infrastructure to separate files
     - Add support for PCI interrupt vectors
     - Implement TryInto<IrqRequest<'a>> for IrqVector<'a> to convert an
       IrqVector bound to specific pci::Device into an IrqRequest bound to the
       same pci::Device's parent Device
     - Leverage pin_init_scope() to get rid of redundant Result in IRQ methods
 
   - PinInit:
     - Add {pin_}init_scope() to execute code before creating an initializer
 
   - Platform:
     - Leverage pin_init_scope() to get rid of redundant Result in IRQ methods
 
   - Timekeeping:
     - Implement abstraction of udelay()
 
   - Uaccess:
     - Implement read_slice_partial() and read_slice_file() for UserSliceReader
     - Implement write_slice_partial() and write_slice_file() for UserSliceWriter
 
 - sysfs
   - Prepare the constification of struct attribute
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQS2q/xV6QjXAdC7k+1FlHeO1qrKLgUCaTAehwAKCRBFlHeO1qrK
 LmzvAP0TWwKUGIduccknIa1AFvBM92lWVZptSysotv3SLFZq3wD9GBLIENt1DkEk
 s+GBqbobPgoyaodaysqLQ/SNqF9TcAM=
 =Wutw
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core

Pull driver core updates from Danilo Krummrich:
 "Arch Topology:
   - Move parse_acpi_topology() from arm64 to common code for reuse in
     RISC-V

  CPU:
   - Expose housekeeping CPUs through /sys/devices/system/cpu/housekeeping
   - Print a newline (or 0x0A) instead of '(null)' reading
     /sys/devices/system/cpu/nohz_full when nohz_full= is not set

  debugfs
   - Remove (broken) 'no-mount' mode
   - Remove redundant access mode checks in debugfs_get_tree() and
     debugfs_create_*() functions

  Devres:
   - Remove unused devm_free_percpu() helper
   - Move devm_alloc_percpu() from device.h to devres.h

  Firmware Loader:
   - Replace simple_strtol() with kstrtoint()
   - Do not call cancel_store() when no upload is in progress

  kernfs:
   - Increase struct super_block::maxbytes to MAX_LFS_FILESIZE
   - Fix a missing unwind path in __kernfs_new_node()

  Misc:
   - Increase the name size in struct auxiliary_device_id to 40
     characters
   - Replace system_unbound_wq with system_dfl_wq and add WQ_PERCPU to
     alloc_workqueue()

  Platform:
   - Replace ERR_PTR() with IOMEM_ERR_PTR() in platform ioremap
     functions

  Rust:
   - Auxiliary:
      - Unregister auxiliary device on parent device unbind
      - Move parent() to impl Device; implement device context aware
        parent() for Device<Bound>
      - Illustrate how to safely obtain a driver's device private data
        when calling from an auxiliary driver into the parant device
        driver

   - DebugFs:
      - Implement support for binary large objects

   - Device:
      - Let probe() return the driver's device private data as pinned
        initializer, i.e. impl PinInit<Self, Error>
      - Implement safe accessor for a driver's device private data for
        Device<Bound> (returned reference can't out-live driver binding
        and guarantees the correct private data type)
      - Implement AsBusDevice trait, to be used by class device
        abstractions to derive the bus device type of the parent device

   - DMA:
      - Store raw pointer of allocation as NonNull
      - Use start_ptr() and start_ptr_mut() to inherit correct
        mutability of self

   - FS:
      - Add file::Offset type alias

   - I2C:
      - Add abstractions for I2C device / driver infrastructure
      - Implement abstractions for manual I2C device registrations

   - I/O:
      - Use "kernel vertical" style for imports
      - Define ResourceSize as resource_size_t
      - Move ResourceSize to top-level I/O module
      - Add type alias for phys_addr_t
      - Implement Rust version of read_poll_timeout_atomic()

   - PCI:
      - Use "kernel vertical" style for imports
      - Move I/O and IRQ infrastructure to separate files
      - Add support for PCI interrupt vectors
      - Implement TryInto<IrqRequest<'a>> for IrqVector<'a> to convert
        an IrqVector bound to specific pci::Device into an IrqRequest
        bound to the same pci::Device's parent Device
      - Leverage pin_init_scope() to get rid of redundant Result in IRQ
        methods

   - PinInit:
      - Add {pin_}init_scope() to execute code before creating an
        initializer

   - Platform:
      - Leverage pin_init_scope() to get rid of redundant Result in IRQ
        methods

   - Timekeeping:
      - Implement abstraction of udelay()

   - Uaccess:
      - Implement read_slice_partial() and read_slice_file() for
        UserSliceReader
      - Implement write_slice_partial() and write_slice_file() for
        UserSliceWriter

  sysfs:
   - Prepare the constification of struct attribute"

* tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (75 commits)
  rust: pci: fix build failure when CONFIG_PCI_MSI is disabled
  debugfs: Fix default access mode config check
  debugfs: Remove broken no-mount mode
  debugfs: Remove redundant access mode checks
  driver core: Check drivers_autoprobe for all added devices
  driver core: WQ_PERCPU added to alloc_workqueue users
  driver core: replace use of system_unbound_wq with system_dfl_wq
  tick/nohz: Expose housekeeping CPUs in sysfs
  tick/nohz: avoid showing '(null)' if nohz_full= not set
  sysfs/cpu: Use DEVICE_ATTR_RO for nohz_full attribute
  kernfs: fix memory leak of kernfs_iattrs in __kernfs_new_node
  fs/kernfs: raise sb->maxbytes to MAX_LFS_FILESIZE
  mod_devicetable: Bump auxiliary_device_id name size
  sysfs: simplify attribute definition macros
  samples/kobject: constify 'struct foo_attribute'
  samples/kobject: add is_visible() callback to attribute group
  sysfs: attribute_group: enable const variants of is_visible()
  sysfs: introduce __SYSFS_FUNCTION_ALTERNATIVE()
  sysfs: transparently handle const pointers in ATTRIBUTE_GROUPS()
  sysfs: attribute_group: allow registration of const attribute
  ...
This commit is contained in:
Linus Torvalds 2025-12-05 21:29:02 -08:00
commit 416f99c3b1
74 changed files with 2872 additions and 700 deletions

View file

@ -37,10 +37,11 @@ struct foo_obj {
/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
struct attribute attr;
ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
ssize_t (*show)(struct foo_obj *foo, const struct foo_attribute *attr, char *buf);
ssize_t (*store)(struct foo_obj *foo, const struct foo_attribute *attr,
const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)
#define to_foo_attr(x) container_of_const(x, struct foo_attribute, attr)
/*
* The default show function that must be passed to sysfs. This will be
@ -53,7 +54,7 @@ static ssize_t foo_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
struct foo_attribute *attribute;
const struct foo_attribute *attribute;
struct foo_obj *foo;
attribute = to_foo_attr(attr);
@ -73,7 +74,7 @@ static ssize_t foo_attr_store(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t len)
{
struct foo_attribute *attribute;
const struct foo_attribute *attribute;
struct foo_obj *foo;
attribute = to_foo_attr(attr);
@ -109,13 +110,13 @@ static void foo_release(struct kobject *kobj)
/*
* The "foo" file where the .foo variable is read from and written to.
*/
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
static ssize_t foo_show(struct foo_obj *foo_obj, const struct foo_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", foo_obj->foo);
}
static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
static ssize_t foo_store(struct foo_obj *foo_obj, const struct foo_attribute *attr,
const char *buf, size_t count)
{
int ret;
@ -128,14 +129,14 @@ static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
}
/* Sysfs attributes cannot be world-writable. */
static struct foo_attribute foo_attribute =
static const struct foo_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
/*
* More complex function where we determine which variable is being accessed by
* looking at the attribute for the "baz" and "bar" files.
*/
static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
static ssize_t b_show(struct foo_obj *foo_obj, const struct foo_attribute *attr,
char *buf)
{
int var;
@ -147,7 +148,7 @@ static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
return sysfs_emit(buf, "%d\n", var);
}
static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
static ssize_t b_store(struct foo_obj *foo_obj, const struct foo_attribute *attr,
const char *buf, size_t count)
{
int var, ret;
@ -163,22 +164,37 @@ static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
return count;
}
static struct foo_attribute baz_attribute =
static const struct foo_attribute baz_attribute =
__ATTR(baz, 0664, b_show, b_store);
static struct foo_attribute bar_attribute =
static const struct foo_attribute bar_attribute =
__ATTR(bar, 0664, b_show, b_store);
/*
* Create a group of attributes so that we can create and destroy them all
* at once.
*/
static struct attribute *foo_default_attrs[] = {
static const struct attribute *const foo_default_attrs[] = {
&foo_attribute.attr,
&baz_attribute.attr,
&bar_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
ATTRIBUTE_GROUPS(foo_default);
static umode_t foo_default_attrs_is_visible(struct kobject *kobj,
const struct attribute *attr,
int n)
{
/* Hide attributes with the same name as the kobject. */
if (strcmp(kobject_name(kobj), attr->name) == 0)
return 0;
return attr->mode;
}
static const struct attribute_group foo_default_group = {
.attrs_const = foo_default_attrs,
.is_visible_const = foo_default_attrs_is_visible,
};
__ATTRIBUTE_GROUPS(foo_default);
/*
* Our own ktype for our kobjects. Here we specify our sysfs ops, the

View file

@ -84,6 +84,29 @@ config SAMPLE_RUST_DEBUGFS_SCOPED
If unsure, say N.
config SAMPLE_RUST_DRIVER_I2C
tristate "I2C Driver"
depends on I2C=y
help
This option builds the Rust I2C driver sample.
To compile this as a module, choose M here:
the module will be called rust_driver_i2c.
If unsure, say N.
config SAMPLE_RUST_I2C_CLIENT
tristate "I2C Client Registration"
depends on I2C=y
help
This option builds the Rust I2C client manual creation
sample.
To compile this as a module, choose M here:
the module will be called rust_i2c_client.
If unsure, say N.
config SAMPLE_RUST_DRIVER_PCI
tristate "PCI Driver"
depends on PCI
@ -91,7 +114,7 @@ config SAMPLE_RUST_DRIVER_PCI
This option builds the Rust PCI driver sample.
To compile this as a module, choose M here:
the module will be called driver_pci.
the module will be called rust_driver_pci.
If unsure, say N.

View file

@ -7,6 +7,8 @@ obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) += rust_debugfs.o
obj-$(CONFIG_SAMPLE_RUST_DEBUGFS_SCOPED) += rust_debugfs_scoped.o
obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) += rust_driver_i2c.o
obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT) += rust_i2c_client.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_USB) += rust_driver_usb.o

View file

@ -36,6 +36,7 @@ use kernel::c_str;
use kernel::debugfs::{Dir, File};
use kernel::new_mutex;
use kernel::prelude::*;
use kernel::sizes::*;
use kernel::sync::atomic::{Atomic, Relaxed};
use kernel::sync::Mutex;
use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef};
@ -60,6 +61,10 @@ struct RustDebugFs {
counter: File<Atomic<usize>>,
#[pin]
inner: File<Mutex<Inner>>,
#[pin]
array_blob: File<Mutex<[u8; 4]>>,
#[pin]
vector_blob: File<Mutex<KVec<u8>>>,
}
#[derive(Debug)]
@ -104,16 +109,17 @@ impl platform::Driver for RustDebugFs {
fn probe(
pdev: &platform::Device<Core>,
_info: Option<&Self::IdInfo>,
) -> Result<Pin<KBox<Self>>> {
let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?;
// We can still mutate fields through the files which are atomic or mutexed:
result.counter.store(91, Relaxed);
{
let mut guard = result.inner.lock();
guard.x = guard.y;
guard.y = 42;
}
Ok(result)
) -> impl PinInit<Self, Error> {
RustDebugFs::new(pdev).pin_chain(|this| {
this.counter.store(91, Relaxed);
{
let mut guard = this.inner.lock();
guard.x = guard.y;
guard.y = 42;
}
Ok(())
})
}
}
@ -141,6 +147,14 @@ impl RustDebugFs {
),
counter <- Self::build_counter(&debugfs),
inner <- Self::build_inner(&debugfs),
array_blob <- debugfs.read_write_binary_file(
c_str!("array_blob"),
new_mutex!([0x62, 0x6c, 0x6f, 0x62]),
),
vector_blob <- debugfs.read_write_binary_file(
c_str!("vector_blob"),
new_mutex!(kernel::kvec!(0x42; SZ_4K)?),
),
_debugfs: debugfs,
pdev: pdev.into(),
}

View file

@ -8,6 +8,7 @@
use kernel::debugfs::{Dir, Scope};
use kernel::prelude::*;
use kernel::sizes::*;
use kernel::sync::atomic::Atomic;
use kernel::sync::Mutex;
use kernel::{c_str, new_mutex, str::CString};
@ -66,18 +67,22 @@ fn create_file_write(
GFP_KERNEL,
)?;
}
let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;
let scope = KBox::pin_init(
mod_data
.device_dir
.scope(DeviceData { name, nums }, &file_name, |dev_data, dir| {
mod_data.device_dir.scope(
DeviceData { name, nums, blob },
&file_name,
|dev_data, dir| {
for (idx, val) in dev_data.nums.iter().enumerate() {
let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
return;
};
dir.read_write_file(&name, val);
}
}),
dir.read_write_binary_file(c_str!("blob"), &dev_data.blob);
},
),
GFP_KERNEL,
)?;
(*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
@ -110,6 +115,7 @@ impl ModuleData {
struct DeviceData {
name: CString,
nums: KVec<Atomic<usize>>,
blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,
}
fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {

View file

@ -55,36 +55,33 @@ impl pci::Driver for DmaSampleDriver {
type IdInfo = ();
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_info!(pdev.as_ref(), "Probe DMA test driver.\n");
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
pin_init::pin_init_scope(move || {
dev_info!(pdev.as_ref(), "Probe DMA test driver.\n");
let mask = DmaMask::new::<64>();
let mask = DmaMask::new::<64>();
// SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.
unsafe { pdev.dma_set_mask_and_coherent(mask)? };
// SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.
unsafe { pdev.dma_set_mask_and_coherent(mask)? };
let ca: CoherentAllocation<MyStruct> =
CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
let ca: CoherentAllocation<MyStruct> =
CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
for (i, value) in TEST_VALUES.into_iter().enumerate() {
kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?;
}
for (i, value) in TEST_VALUES.into_iter().enumerate() {
kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?;
}
let size = 4 * page::PAGE_SIZE;
let pages = VVec::with_capacity(size, GFP_KERNEL)?;
let size = 4 * page::PAGE_SIZE;
let pages = VVec::with_capacity(size, GFP_KERNEL)?;
let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
let drvdata = KBox::pin_init(
try_pin_init!(Self {
Ok(try_pin_init!(Self {
pdev: pdev.into(),
ca,
sgt <- sgt,
}),
GFP_KERNEL,
)?;
Ok(drvdata)
}))
})
}
}

View file

@ -5,9 +5,17 @@
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{
auxiliary, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule,
auxiliary, c_str,
device::{Bound, Core},
devres::Devres,
driver,
error::Error,
pci,
prelude::*,
InPlaceModule,
};
use core::any::TypeId;
use pin_init::PinInit;
const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
@ -27,7 +35,7 @@ impl auxiliary::Driver for AuxiliaryDriver {
const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
dev_info!(
adev.as_ref(),
"Probing auxiliary driver for auxiliary device with id={}\n",
@ -36,14 +44,17 @@ impl auxiliary::Driver for AuxiliaryDriver {
ParentDriver::connect(adev)?;
let this = KBox::new(Self, GFP_KERNEL)?;
Ok(this.into())
Ok(Self)
}
}
#[pin_data]
struct ParentDriver {
_reg: [auxiliary::Registration; 2],
private: TypeId,
#[pin]
_reg0: Devres<auxiliary::Registration>,
#[pin]
_reg1: Devres<auxiliary::Registration>,
}
kernel::pci_device_table!(
@ -58,35 +69,35 @@ impl pci::Driver for ParentDriver {
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())
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
private: TypeId::of::<Self>(),
_reg0 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME),
_reg1 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME),
})
}
}
impl ParentDriver {
fn connect(adev: &auxiliary::Device) -> Result<()> {
let parent = adev.parent().ok_or(EINVAL)?;
let pdev: &pci::Device = parent.try_into()?;
fn connect(adev: &auxiliary::Device<Bound>) -> Result {
let dev = adev.parent();
let pdev: &pci::Device<Bound> = dev.try_into()?;
let drvdata = dev.drvdata::<Self>()?;
let vendor = pdev.vendor_id();
dev_info!(
adev.as_ref(),
dev,
"Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
adev.id(),
vendor,
pdev.vendor_id(),
pdev.device_id()
);
dev_info!(
dev,
"We have access to the private data of {:?}.\n",
drvdata.private
);
Ok(())
}
}

View file

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
//! Rust I2C driver sample.
use kernel::{
acpi,
c_str,
device::Core,
i2c,
of,
prelude::*, //
};
struct SampleDriver;
kernel::acpi_device_table! {
ACPI_TABLE,
MODULE_ACPI_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), 0)]
}
kernel::i2c_device_table! {
I2C_TABLE,
MODULE_I2C_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(i2c::DeviceId::new(c_str!("rust_driver_i2c")), 0)]
}
kernel::of_device_table! {
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(of::DeviceId::new(c_str!("test,rust_driver_i2c")), 0)]
}
impl i2c::Driver for SampleDriver {
type IdInfo = u32;
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
fn probe(
idev: &i2c::I2cClient<Core>,
info: Option<&Self::IdInfo>,
) -> impl PinInit<Self, Error> {
let dev = idev.as_ref();
dev_info!(dev, "Probe Rust I2C driver sample.\n");
if let Some(info) = info {
dev_info!(dev, "Probed with info: '{}'.\n", info);
}
Ok(Self)
}
fn shutdown(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n");
}
fn unbind(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n");
}
}
kernel::module_i2c_driver! {
type: SampleDriver,
name: "rust_driver_i2c",
authors: ["Igor Korotin"],
description: "Rust I2C driver",
license: "GPL v2",
}

View file

@ -65,35 +65,34 @@ impl pci::Driver for SampleDriver {
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
let vendor = pdev.vendor_id();
dev_dbg!(
pdev.as_ref(),
"Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
vendor,
pdev.device_id()
);
fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
pin_init::pin_init_scope(move || {
let vendor = pdev.vendor_id();
dev_dbg!(
pdev.as_ref(),
"Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
vendor,
pdev.device_id()
);
pdev.enable_device_mem()?;
pdev.set_master();
pdev.enable_device_mem()?;
pdev.set_master();
let drvdata = KBox::pin_init(
try_pin_init!(Self {
Ok(try_pin_init!(Self {
bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")),
pdev: pdev.into(),
index: *info,
}),
GFP_KERNEL,
)?;
_: {
let bar = bar.access(pdev.as_ref())?;
let bar = drvdata.bar.access(pdev.as_ref())?;
dev_info!(
pdev.as_ref(),
"pci-testdev data-match count: {}\n",
Self::testdev(info, bar)?
);
Ok(drvdata)
dev_info!(
pdev.as_ref(),
"pci-testdev data-match count: {}\n",
Self::testdev(info, bar)?
);
},
pdev: pdev.into(),
}))
})
}
fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {

View file

@ -103,7 +103,7 @@ impl platform::Driver for SampleDriver {
fn probe(
pdev: &platform::Device<Core>,
info: Option<&Self::IdInfo>,
) -> Result<Pin<KBox<Self>>> {
) -> impl PinInit<Self, Error> {
let dev = pdev.as_ref();
dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
@ -116,9 +116,7 @@ impl platform::Driver for SampleDriver {
Self::properties_parse(dev)?;
}
let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?;
Ok(drvdata.into())
Ok(Self { pdev: pdev.into() })
}
}

View file

@ -24,12 +24,11 @@ impl usb::Driver for SampleDriver {
intf: &usb::Interface<Core>,
_id: &usb::DeviceId,
_info: &Self::IdInfo,
) -> Result<Pin<KBox<Self>>> {
) -> impl PinInit<Self, Error> {
let dev: &device::Device<Core> = intf.as_ref();
dev_info!(dev, "Rust USB driver sample probed\n");
let drvdata = KBox::new(Self { _intf: intf.into() }, GFP_KERNEL)?;
Ok(drvdata.into())
Ok(Self { _intf: intf.into() })
}
fn disconnect(intf: &usb::Interface<Core>, _data: Pin<&Self>) {

View file

@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0
//! Rust I2C client registration sample.
//!
//! An I2C client in Rust cannot exist on its own. To register a new I2C client,
//! it must be bound to a parent device. In this sample driver, a platform device
//! is used as the parent.
//!
//! ACPI match table test
//!
//! This demonstrates how to test an ACPI-based Rust I2C client registration driver
//! using QEMU with a custom SSDT.
//!
//! Steps:
//!
//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
//!
//! ```asl
//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
//! {
//! Scope (\_SB)
//! {
//! Device (T432)
//! {
//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match
//! Name (_UID, 1)
//! Name (_STA, 0x0F) // Device present, enabled
//! Name (_CRS, ResourceTemplate ()
//! {
//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
//! })
//! }
//! }
//! }
//! ```
//!
//! 2. **Compile the table**:
//!
//! ```sh
//! iasl -tc ssdt.dsl
//! ```
//!
//! This generates `ssdt.aml`
//!
//! 3. **Run QEMU** with the compiled AML file:
//!
//! ```sh
//! qemu-system-x86_64 -m 512M \
//! -enable-kvm \
//! -kernel path/to/bzImage \
//! -append "root=/dev/sda console=ttyS0" \
//! -hda rootfs.img \
//! -serial stdio \
//! -acpitable file=ssdt.aml
//! ```
//!
//! Requirements:
//! - The `rust_driver_platform` must be present either:
//! - built directly into the kernel (`bzImage`), or
//! - available as a `.ko` file and loadable from `rootfs.img`
//!
//! 4. **Verify it worked** by checking `dmesg`:
//!
//! ```
//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'.
//! ```
//!
use kernel::{
acpi,
c_str,
device,
devres::Devres,
i2c,
of,
platform,
prelude::*,
sync::aref::ARef, //
};
#[pin_data]
struct SampleDriver {
parent_dev: ARef<platform::Device>,
#[pin]
_reg: Devres<i2c::Registration>,
}
kernel::of_device_table!(
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(of::DeviceId::new(c_str!("test,rust-device")), ())]
);
kernel::acpi_device_table!(
ACPI_TABLE,
MODULE_ACPI_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
);
const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30;
const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0;
const BOARD_INFO: i2c::I2cBoardInfo =
i2c::I2cBoardInfo::new(c_str!("rust_driver_i2c"), SAMPLE_I2C_CLIENT_ADDR);
impl platform::Driver for SampleDriver {
type IdInfo = ();
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
fn probe(
pdev: &platform::Device<device::Core>,
_info: Option<&Self::IdInfo>,
) -> impl PinInit<Self, Error> {
dev_info!(
pdev.as_ref(),
"Probe Rust I2C Client registration sample.\n"
);
kernel::try_pin_init!( Self {
parent_dev: pdev.into(),
_reg <- {
let adapter = i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?;
i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref())
}
})
}
fn unbind(pdev: &platform::Device<device::Core>, _this: Pin<&Self>) {
dev_info!(
pdev.as_ref(),
"Unbind Rust I2C Client registration sample.\n"
);
}
}
kernel::module_platform_driver! {
type: SampleDriver,
name: "rust_device_i2c",
authors: ["Danilo Krummrich", "Igor Korotin"],
description: "Rust I2C client registration",
license: "GPL v2",
}