From 0242623384c767b1156b61b67894b4ecf6682b8b Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 16 Oct 2025 14:55:28 +0200 Subject: rust: driver: let probe() return impl PinInit The driver model defines the lifetime of the private data stored in (and owned by) a bus device to be valid from when the driver is bound to a device (i.e. from successful probe()) until the driver is unbound from the device. This is already taken care of by the Rust implementation of the driver model. However, we still ask drivers to return a Result>> from probe(). Unlike in C, where we do not have the concept of initializers, but rather deal with uninitialized memory, drivers can just return an impl PinInit instead. This contributes to more clarity to the fact that a driver returns it's device private data in probe() and the Rust driver model owns the data, manages the lifetime and - considering the lifetime - provides (safe) accessors for the driver. Hence, let probe() functions return an impl PinInit instead of Result>>. Reviewed-by: Alice Ryhl Acked-by: Viresh Kumar Reviewed-by: Alexandre Courbot Acked-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- samples/rust/rust_debugfs.rs | 21 +++++++------- samples/rust/rust_dma.rs | 37 +++++++++++------------- samples/rust/rust_driver_auxiliary.rs | 25 ++++++----------- samples/rust/rust_driver_pci.rs | 53 +++++++++++++++++------------------ samples/rust/rust_driver_platform.rs | 6 ++-- samples/rust/rust_driver_usb.rs | 5 ++-- 6 files changed, 67 insertions(+), 80 deletions(-) (limited to 'samples') diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 82b61a15a34b..d3f50f344856 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -106,16 +106,17 @@ impl platform::Driver for RustDebugFs { fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, - ) -> Result>> { - 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, Ordering::Relaxed); - { - let mut guard = result.inner.lock(); - guard.x = guard.y; - guard.y = 42; - } - Ok(result) + ) -> impl PinInit { + RustDebugFs::new(pdev).pin_chain(|this| { + this.counter.store(91, Ordering::Relaxed); + { + let mut guard = this.inner.lock(); + guard.x = guard.y; + guard.y = 42; + } + + Ok(()) + }) } } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 4d324f06cc2a..f53bce2a73e3 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -55,36 +55,33 @@ impl pci::Driver for DmaSampleDriver { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + 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 = - CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; + let ca: CoherentAllocation = + 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) + })) + }) } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 55ece336ee45..0e221abe4936 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -27,7 +27,7 @@ impl auxiliary::Driver for AuxiliaryDriver { const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> Result>> { + fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { dev_info!( adev.as_ref(), "Probing auxiliary driver for auxiliary device with id={}\n", @@ -36,9 +36,7 @@ impl auxiliary::Driver for AuxiliaryDriver { ParentDriver::connect(adev)?; - let this = KBox::new(Self, GFP_KERNEL)?; - - Ok(this.into()) + Ok(Self) } } @@ -58,18 +56,13 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - 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, _info: &Self::IdInfo) -> impl PinInit { + Ok(Self { + _reg: [ + auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?, + auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?, + ], + }) } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 55a683c39ed9..5823787bea8e 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -65,35 +65,34 @@ impl pci::Driver for SampleDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> Result>> { - 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(); - - let drvdata = KBox::pin_init( - try_pin_init!(Self { + fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit { + 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(); + + 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 = drvdata.bar.access(pdev.as_ref())?; - dev_info!( - pdev.as_ref(), - "pci-testdev data-match count: {}\n", - Self::testdev(info, bar)? - ); - - Ok(drvdata) + _: { + let bar = bar.access(pdev.as_ref())?; + + dev_info!( + pdev.as_ref(), + "pci-testdev data-match count: {}\n", + Self::testdev(info, bar)? + ); + }, + pdev: pdev.into(), + })) + }) } fn unbind(pdev: &pci::Device, this: Pin<&Self>) { diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 6473baf4f120..d63adb422ba6 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -103,7 +103,7 @@ impl platform::Driver for SampleDriver { fn probe( pdev: &platform::Device, info: Option<&Self::IdInfo>, - ) -> Result>> { + ) -> impl PinInit { 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() }) } } diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index 5c396f421de7..4eaad14867b2 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -24,12 +24,11 @@ impl usb::Driver for SampleDriver { intf: &usb::Interface, _id: &usb::DeviceId, _info: &Self::IdInfo, - ) -> Result>> { + ) -> impl PinInit { let dev: &device::Device = 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, _data: Pin<&Self>) { -- cgit v1.2.3 From 589b061975db3c7e87b819cc9a8006eb99ac4b5f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:25 +0200 Subject: rust: auxiliary: consider auxiliary devices always have a parent An auxiliary device is guaranteed to always have a parent device (both in C and Rust), hence don't return an Option<&auxiliary::Device> in auxiliary::Device::parent(). Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nova/file.rs | 2 +- rust/kernel/auxiliary.rs | 7 ++++--- samples/rust/rust_driver_auxiliary.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'samples') diff --git a/drivers/gpu/drm/nova/file.rs b/drivers/gpu/drm/nova/file.rs index 90b9d2d0ec4a..a3b7bd36792c 100644 --- a/drivers/gpu/drm/nova/file.rs +++ b/drivers/gpu/drm/nova/file.rs @@ -28,7 +28,7 @@ impl File { _file: &drm::File, ) -> Result { let adev = &dev.adev; - let parent = adev.parent().ok_or(ENOENT)?; + let parent = adev.parent(); let pdev: &pci::Device = parent.try_into()?; let value = match getparam.param as u32 { diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index a6a2b23befce..e5bddb738d58 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -215,9 +215,10 @@ impl Device { unsafe { (*self.as_raw()).id } } - /// Returns a reference to the parent [`device::Device`], if any. - pub fn parent(&self) -> Option<&device::Device> { - self.as_ref().parent() + /// Returns a reference to the parent [`device::Device`]. + pub fn parent(&self) -> &device::Device { + // SAFETY: A `struct auxiliary_device` always has a parent. + unsafe { self.as_ref().parent().unwrap_unchecked() } } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 0e221abe4936..2e9afeb83d4f 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -68,7 +68,7 @@ impl pci::Driver for ParentDriver { impl ParentDriver { fn connect(adev: &auxiliary::Device) -> Result<()> { - let parent = adev.parent().ok_or(EINVAL)?; + let parent = adev.parent(); let pdev: &pci::Device = parent.try_into()?; let vendor = pdev.vendor_id(); -- cgit v1.2.3 From e4e679c8608e5c747081cc6ce63ee0b0e524c68d Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:26 +0200 Subject: rust: auxiliary: unregister on parent device unbind Guarantee that an auxiliary driver will be unbound before its parent is unbound; there is no point in operating an auxiliary device whose parent has been unbound. In practice, this guarantee allows us to assume that for a bound auxiliary device, also the parent device is bound. This is useful when an auxiliary driver calls into its parent, since it allows the parent to directly access device resources and its device private data due to the guaranteed bound device context. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 8 ++-- rust/kernel/auxiliary.rs | 89 ++++++++++++++++++++--------------- samples/rust/rust_driver_auxiliary.rs | 17 ++++--- 3 files changed, 66 insertions(+), 48 deletions(-) (limited to 'samples') diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index a83b86199182..ca0d5f8ad54b 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -3,6 +3,7 @@ use kernel::{ auxiliary, c_str, device::Core, + devres::Devres, pci, pci::{Class, ClassMask, Vendor}, prelude::*, @@ -16,7 +17,8 @@ use crate::gpu::Gpu; pub(crate) struct NovaCore { #[pin] pub(crate) gpu: Gpu, - _reg: auxiliary::Registration, + #[pin] + _reg: Devres, } const BAR0_SIZE: usize = SZ_16M; @@ -65,12 +67,12 @@ impl pci::Driver for NovaCore { Ok(try_pin_init!(Self { gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), - _reg: auxiliary::Registration::new( + _reg <- auxiliary::Registration::new( pdev.as_ref(), c_str!("nova-drm"), 0, // TODO[XARR]: Once it lands, use XArray; for now we don't use the ID. crate::MODULE_NAME - )?, + ), })) }) } diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index e5bddb738d58..8c0a2472c26a 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -7,6 +7,7 @@ use crate::{ bindings, container_of, device, device_id::{RawDeviceId, RawDeviceIdIndex}, + devres::Devres, driver, error::{from_result, to_result, Result}, prelude::*, @@ -279,8 +280,8 @@ unsafe impl Sync for Device {} /// The registration of an auxiliary device. /// -/// This type represents the registration of a [`struct auxiliary_device`]. When an instance of this -/// type is dropped, its respective auxiliary device will be unregistered from the system. +/// This type represents the registration of a [`struct auxiliary_device`]. When its parent device +/// is unbound, the corresponding auxiliary device will be unregistered from the system. /// /// # Invariants /// @@ -290,44 +291,56 @@ pub struct Registration(NonNull); impl Registration { /// Create and register a new auxiliary device. - pub fn new(parent: &device::Device, name: &CStr, id: u32, modname: &CStr) -> Result { - let boxed = KBox::new(Opaque::::zeroed(), GFP_KERNEL)?; - let adev = boxed.get(); + pub fn new<'a>( + parent: &'a device::Device, + name: &'a CStr, + id: u32, + modname: &'a CStr, + ) -> impl PinInit, Error> + 'a { + pin_init::pin_init_scope(move || { + let boxed = KBox::new(Opaque::::zeroed(), GFP_KERNEL)?; + let adev = boxed.get(); + + // SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization. + unsafe { + (*adev).dev.parent = parent.as_raw(); + (*adev).dev.release = Some(Device::release); + (*adev).name = name.as_char_ptr(); + (*adev).id = id; + } - // SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization. - unsafe { - (*adev).dev.parent = parent.as_raw(); - (*adev).dev.release = Some(Device::release); - (*adev).name = name.as_char_ptr(); - (*adev).id = id; - } - - // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, - // which has not been initialized yet. - unsafe { bindings::auxiliary_device_init(adev) }; - - // Now that `adev` is initialized, leak the `Box`; the corresponding memory will be freed - // by `Device::release` when the last reference to the `struct auxiliary_device` is dropped. - let _ = KBox::into_raw(boxed); - - // SAFETY: - // - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which has - // been initialialized, - // - `modname.as_char_ptr()` is a NULL terminated string. - let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) }; - if ret != 0 { // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, - // which has been initialialized. - unsafe { bindings::auxiliary_device_uninit(adev) }; - - return Err(Error::from_errno(ret)); - } - - // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated successfully. - // - // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is called, - // which happens in `Self::drop()`. - Ok(Self(unsafe { NonNull::new_unchecked(adev) })) + // which has not been initialized yet. + unsafe { bindings::auxiliary_device_init(adev) }; + + // Now that `adev` is initialized, leak the `Box`; the corresponding memory will be + // freed by `Device::release` when the last reference to the `struct auxiliary_device` + // is dropped. + let _ = KBox::into_raw(boxed); + + // SAFETY: + // - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which + // has been initialized, + // - `modname.as_char_ptr()` is a NULL terminated string. + let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) }; + if ret != 0 { + // SAFETY: `adev` is guaranteed to be a valid pointer to a + // `struct auxiliary_device`, which has been initialized. + unsafe { bindings::auxiliary_device_uninit(adev) }; + + return Err(Error::from_errno(ret)); + } + + // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated + // successfully. + // + // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is + // called, which happens in `Self::drop()`. + Ok(Devres::new( + parent, + Self(unsafe { NonNull::new_unchecked(adev) }), + )) + }) } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 2e9afeb83d4f..95c552ee9489 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -5,7 +5,8 @@ //! 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::Core, devres::Devres, driver, error::Error, pci, prelude::*, + InPlaceModule, }; use pin_init::PinInit; @@ -40,8 +41,12 @@ impl auxiliary::Driver for AuxiliaryDriver { } } +#[pin_data] struct ParentDriver { - _reg: [auxiliary::Registration; 2], + #[pin] + _reg0: Devres, + #[pin] + _reg1: Devres, } kernel::pci_device_table!( @@ -57,11 +62,9 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { - Ok(Self { - _reg: [ - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?, - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?, - ], + try_pin_init!(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), }) } } -- cgit v1.2.3 From 710ac546883c2cae6e8e7b5dcf7757b8a49d75a1 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:29 +0200 Subject: samples: rust: auxiliary: misc cleanup of ParentDriver::connect() In ParentDriver::connect() rename parent to dev, use it for the dev_info!() call, call pdev.vendor_() directly in the print statement and remove the unnecessary generic type of Result. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- samples/rust/rust_driver_auxiliary.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'samples') diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 95c552ee9489..a5d67d4d9e83 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -70,16 +70,15 @@ impl pci::Driver for ParentDriver { } impl ParentDriver { - fn connect(adev: &auxiliary::Device) -> Result<()> { - let parent = adev.parent(); - let pdev: &pci::Device = parent.try_into()?; + fn connect(adev: &auxiliary::Device) -> Result { + let dev = adev.parent(); + let pdev: &pci::Device = dev.try_into()?; - 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() ); -- cgit v1.2.3 From b0b7301b004301afe920b3d08caa6171dd3f4011 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:30 +0200 Subject: samples: rust: auxiliary: illustrate driver interaction Illustrate how a parent driver of an auxiliary driver can take advantage of the device context guarantees given by the auxiliary bus and subsequently safely derive its device private data. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- samples/rust/rust_driver_auxiliary.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'samples') diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index a5d67d4d9e83..5761ea314f44 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -5,10 +5,17 @@ //! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{ - auxiliary, c_str, device::Core, devres::Devres, driver, error::Error, pci, prelude::*, + 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 = ::NAME; @@ -43,6 +50,7 @@ impl auxiliary::Driver for AuxiliaryDriver { #[pin_data] struct ParentDriver { + private: TypeId, #[pin] _reg0: Devres, #[pin] @@ -63,6 +71,7 @@ impl pci::Driver for ParentDriver { fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { try_pin_init!(Self { + private: TypeId::of::(), _reg0 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME), _reg1 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME), }) @@ -70,9 +79,10 @@ impl pci::Driver for ParentDriver { } impl ParentDriver { - fn connect(adev: &auxiliary::Device) -> Result { + fn connect(adev: &auxiliary::Device) -> Result { let dev = adev.parent(); - let pdev: &pci::Device = dev.try_into()?; + let pdev: &pci::Device = dev.try_into()?; + let drvdata = dev.drvdata::()?; dev_info!( dev, @@ -82,6 +92,12 @@ impl ParentDriver { pdev.device_id() ); + dev_info!( + dev, + "We have access to the private data of {:?}.\n", + drvdata.private + ); + Ok(()) } } -- cgit v1.2.3 From 52af0c37964be5a685cfd7aa4127bdd29d629cc7 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 22 Oct 2025 16:30:42 +0200 Subject: samples: rust: debugfs: add example for blobs Extend the Rust debugfs sample to demonstrate usage of binary file support. The example now shows how to expose both fixed-size arrays and dynamically sized vectors as binary blobs in debugfs. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Reviewed-by: Matthew Maurer Signed-off-by: Danilo Krummrich --- samples/rust/rust_debugfs.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'samples') diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index d3f50f344856..c45b568d951b 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -38,6 +38,7 @@ use kernel::c_str; use kernel::debugfs::{Dir, File}; use kernel::new_mutex; use kernel::prelude::*; +use kernel::sizes::*; use kernel::sync::Mutex; use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; @@ -62,6 +63,10 @@ struct RustDebugFs { counter: File, #[pin] inner: File>, + #[pin] + array_blob: File>, + #[pin] + vector_blob: File>>, } #[derive(Debug)] @@ -144,6 +149,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(), } -- cgit v1.2.3 From f656279afde16afee3ac163b90584ddceacb4e61 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 22 Oct 2025 16:30:44 +0200 Subject: samples: rust: debugfs_scoped: add example for blobs Extend the rust_debugfs_scoped sample to demonstrate how to export a large binary object through a ScopedDir. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Reviewed-by: Matthew Maurer Signed-off-by: Danilo Krummrich --- samples/rust/rust_debugfs_scoped.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'samples') diff --git a/samples/rust/rust_debugfs_scoped.rs b/samples/rust/rust_debugfs_scoped.rs index b0c4e76b123e..c80312cf168d 100644 --- a/samples/rust/rust_debugfs_scoped.rs +++ b/samples/rust/rust_debugfs_scoped.rs @@ -9,6 +9,7 @@ use core::sync::atomic::AtomicUsize; use kernel::debugfs::{Dir, Scope}; use kernel::prelude::*; +use kernel::sizes::*; 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, + blob: Pin>>, } fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit> + '_ { -- cgit v1.2.3 From d05b8e97690fa19be39f0af03e7f117f601b6319 Mon Sep 17 00:00:00 2001 From: Igor Korotin Date: Sun, 16 Nov 2025 16:22:04 +0000 Subject: samples: rust: add Rust I2C sample driver Add a new `rust_driver_i2c` sample, showing how to create a new i2c driver using ACPI/OF/Legacy ID tables. Acked-by: Wolfram Sang Signed-off-by: Igor Korotin Link: https://patch.msgid.link/20251116162204.171518-1-igor.korotin.linux@gmail.com Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 74 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 samples/rust/rust_driver_i2c.rs (limited to 'samples') diff --git a/MAINTAINERS b/MAINTAINERS index a8dfde2f5633..8133987c4b54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11750,6 +11750,7 @@ R: Daniel Almeida L: rust-for-linux@vger.kernel.org S: Maintained F: rust/kernel/i2c.rs +F: samples/rust/rust_driver_i2c.rs I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c376eb899b7a..181466319588 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -84,6 +84,17 @@ 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_DRIVER_PCI tristate "PCI Driver" depends on PCI diff --git a/samples/rust/Makefile b/samples/rust/Makefile index cf8422f8f219..aec865e2adef 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -7,6 +7,7 @@ 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_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 diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs new file mode 100644 index 000000000000..ecefeca3e22f --- /dev/null +++ b/samples/rust/rust_driver_i2c.rs @@ -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, + ::IdInfo, + [(acpi::DeviceId::new(c_str!("LNUXBEEF")), 0)] +} + +kernel::i2c_device_table! { + I2C_TABLE, + MODULE_I2C_TABLE, + ::IdInfo, + [(i2c::DeviceId::new(c_str!("rust_driver_i2c")), 0)] +} + +kernel::of_device_table! { + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("test,rust_driver_i2c")), 0)] +} + +impl i2c::Driver for SampleDriver { + type IdInfo = u32; + + const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); + const I2C_ID_TABLE: Option> = Some(&I2C_TABLE); + const OF_ID_TABLE: Option> = Some(&OF_TABLE); + + fn probe( + idev: &i2c::I2cClient, + info: Option<&Self::IdInfo>, + ) -> impl PinInit { + 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, _this: Pin<&Self>) { + dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n"); + } + + fn unbind(idev: &i2c::I2cClient, _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", +} -- cgit v1.2.3 From 13ae55e24a854efd33eb84bbd19a74b933228ccd Mon Sep 17 00:00:00 2001 From: Igor Korotin Date: Sun, 16 Nov 2025 16:22:10 +0000 Subject: samples: rust: add Rust I2C client registration sample Add a new `rust_i2c_client` sample, showing how to create a new i2c client using `i2c::Registration` Acked-by: Wolfram Sang Signed-off-by: Igor Korotin Link: https://patch.msgid.link/20251116162210.171542-1-igor.korotin.linux@gmail.com [ * Remove dependency to I2C_CHARDEV, depend on I2C=y. * Remove unnecessary impl Drop for SampleDriver. * Rename i2c::Registration, import Devres. * Fixup module description. * Add new source file to MAINTAINERS. - Danilo ] Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + samples/rust/Kconfig | 12 ++++ samples/rust/Makefile | 1 + samples/rust/rust_i2c_client.rs | 147 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 samples/rust/rust_i2c_client.rs (limited to 'samples') diff --git a/MAINTAINERS b/MAINTAINERS index 8133987c4b54..b71ea515240a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11751,6 +11751,7 @@ L: rust-for-linux@vger.kernel.org S: Maintained F: rust/kernel/i2c.rs F: samples/rust/rust_driver_i2c.rs +F: samples/rust/rust_i2c_client.rs I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 181466319588..b66bed5d3f36 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -95,6 +95,18 @@ config SAMPLE_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 diff --git a/samples/rust/Makefile b/samples/rust/Makefile index aec865e2adef..f65885d1d62b 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -8,6 +8,7 @@ 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 diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs new file mode 100644 index 000000000000..f67938396dce --- /dev/null +++ b/samples/rust/rust_i2c_client.rs @@ -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, + #[pin] + _reg: Devres, +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("test,rust-device")), ())] +); + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::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> = Some(&OF_TABLE); + const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); + + fn probe( + pdev: &platform::Device, + _info: Option<&Self::IdInfo>, + ) -> impl PinInit { + 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, _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", +} -- cgit v1.2.3 From 4635406417bb842862d6322c461743c9b128c444 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Thu, 20 Nov 2025 11:06:09 -0500 Subject: samples: rust: fix module name for Rust PCI driver sample Replace driver_pci -> rust_driver_pci in config SAMPLE_RUST_DRIVER_PCI. Signed-off-by: Peter Colberg Fixes: 685376d18e9a ("samples: rust: add Rust PCI sample driver") Link: https://patch.msgid.link/20251120-rust-driver-pci-fix-module-name-v1-1-430cee310dc0@redhat.com Signed-off-by: Danilo Krummrich --- samples/rust/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'samples') diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b66bed5d3f36..cde1dc9451d3 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -114,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. -- cgit v1.2.3 From c301a2e2d78c2c20d466c7e38568406471ede17d Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 29 Oct 2025 09:12:14 +0100 Subject: samples/kobject: add is_visible() callback to attribute group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was no example for the is_visible() callback so far. It will also become an example and test for the constification of 'struct attribute' later. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20251029-sysfs-const-attr-prep-v5-5-ea7d745acff4@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- samples/kobject/kset-example.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'samples') diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index 579ce150217c..1aac595ed949 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -178,7 +178,22 @@ static struct attribute *foo_default_attrs[] = { &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, + 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 = foo_default_attrs, + .is_visible = foo_default_attrs_is_visible, +}; +__ATTRIBUTE_GROUPS(foo_default); /* * Our own ktype for our kobjects. Here we specify our sysfs ops, the -- cgit v1.2.3 From 2d76fdc1c91a95c23cd3e47a4fd4315f1633019b Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 29 Oct 2025 09:12:15 +0100 Subject: samples/kobject: constify 'struct foo_attribute' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Showcase and test the new 'struct attribute' constification facilities. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20251029-sysfs-const-attr-prep-v5-6-ea7d745acff4@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- samples/kobject/kset-example.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'samples') diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index 1aac595ed949..d0103904e5dd 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -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,16 +164,16 @@ 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, @@ -180,7 +181,7 @@ static struct attribute *foo_default_attrs[] = { }; static umode_t foo_default_attrs_is_visible(struct kobject *kobj, - struct attribute *attr, + const struct attribute *attr, int n) { /* Hide attributes with the same name as the kobject. */ @@ -190,8 +191,8 @@ static umode_t foo_default_attrs_is_visible(struct kobject *kobj, } static const struct attribute_group foo_default_group = { - .attrs = foo_default_attrs, - .is_visible = foo_default_attrs_is_visible, + .attrs_const = foo_default_attrs, + .is_visible_const = foo_default_attrs_is_visible, }; __ATTRIBUTE_GROUPS(foo_default); -- cgit v1.2.3