mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
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:
commit
416f99c3b1
74 changed files with 2872 additions and 700 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>> + '_ {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
74
samples/rust/rust_driver_i2c.rs
Normal file
74
samples/rust/rust_driver_i2c.rs
Normal 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",
|
||||
}
|
||||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
147
samples/rust/rust_i2c_client.rs
Normal file
147
samples/rust/rust_i2c_client.rs
Normal 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",
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue