mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 11:56:58 +00:00
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:
@@ -764,6 +764,17 @@ Description:
|
||||
participate in load balancing. These CPUs are set by
|
||||
boot parameter "isolcpus=".
|
||||
|
||||
What: /sys/devices/system/cpu/housekeeping
|
||||
Date: Oct 2025
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
Description:
|
||||
(RO) the list of logical CPUs that are designated by the kernel as
|
||||
"housekeeping". Each CPU are responsible for handling essential
|
||||
system-wide background tasks, including RCU callbacks, delayed
|
||||
timer callbacks, and unbound workqueues, minimizing scheduling
|
||||
jitter on low-latency, isolated CPUs. These CPUs are set when boot
|
||||
parameter "isolcpus=nohz" or "nohz_full=" is specified.
|
||||
|
||||
What: /sys/devices/system/cpu/crash_hotplug
|
||||
Date: Aug 2023
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
|
||||
@@ -1211,12 +1211,8 @@ Kernel parameters
|
||||
|
||||
debugfs= [KNL,EARLY] This parameter enables what is exposed to
|
||||
userspace and debugfs internal clients.
|
||||
Format: { on, no-mount, off }
|
||||
Format: { on, off }
|
||||
on: All functions are enabled.
|
||||
no-mount:
|
||||
Filesystem is not registered but kernel clients can
|
||||
access APIs and a crashkernel can be used to read
|
||||
its content. There is nothing to mount.
|
||||
off: Filesystem is not registered and clients
|
||||
get a -EPERM as result when trying to register files
|
||||
or directories within debugfs.
|
||||
|
||||
@@ -383,7 +383,6 @@ NET
|
||||
|
||||
PER-CPU MEM
|
||||
devm_alloc_percpu()
|
||||
devm_free_percpu()
|
||||
|
||||
PCI
|
||||
devm_pci_alloc_host_bridge() : managed PCI host bridge allocation
|
||||
|
||||
10
MAINTAINERS
10
MAINTAINERS
@@ -11821,6 +11821,16 @@ F: include/linux/i2c.h
|
||||
F: include/uapi/linux/i2c-*.h
|
||||
F: include/uapi/linux/i2c.h
|
||||
|
||||
I2C SUBSYSTEM [RUST]
|
||||
M: Igor Korotin <igor.korotin.linux@gmail.com>
|
||||
R: Danilo Krummrich <dakr@kernel.org>
|
||||
R: Daniel Almeida <daniel.almeida@collabora.com>
|
||||
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 <andi.shyti@kernel.org>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
|
||||
@@ -36,6 +36,9 @@ void update_freq_counters_refs(void);
|
||||
#define arch_scale_hw_pressure topology_get_hw_pressure
|
||||
#define arch_update_hw_pressure topology_update_hw_pressure
|
||||
|
||||
#undef arch_cpu_is_threaded
|
||||
#define arch_cpu_is_threaded() (read_cpuid_mpidr() & MPIDR_MT_BITMASK)
|
||||
|
||||
#include <asm-generic/topology.h>
|
||||
|
||||
#endif /* _ASM_ARM_TOPOLOGY_H */
|
||||
|
||||
@@ -25,107 +25,6 @@
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/topology.h>
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static bool __init acpi_cpu_is_threaded(int cpu)
|
||||
{
|
||||
int is_threaded = acpi_pptt_cpu_is_thread(cpu);
|
||||
|
||||
/*
|
||||
* if the PPTT doesn't have thread information, assume a homogeneous
|
||||
* machine and return the current CPU's thread state.
|
||||
*/
|
||||
if (is_threaded < 0)
|
||||
is_threaded = read_cpuid_mpidr() & MPIDR_MT_BITMASK;
|
||||
|
||||
return !!is_threaded;
|
||||
}
|
||||
|
||||
struct cpu_smt_info {
|
||||
unsigned int thread_num;
|
||||
int core_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Propagate the topology information of the processor_topology_node tree to the
|
||||
* cpu_topology array.
|
||||
*/
|
||||
int __init parse_acpi_topology(void)
|
||||
{
|
||||
unsigned int max_smt_thread_num = 1;
|
||||
struct cpu_smt_info *entry;
|
||||
struct xarray hetero_cpu;
|
||||
unsigned long hetero_id;
|
||||
int cpu, topology_id;
|
||||
|
||||
if (acpi_disabled)
|
||||
return 0;
|
||||
|
||||
xa_init(&hetero_cpu);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
topology_id = find_acpi_cpu_topology(cpu, 0);
|
||||
if (topology_id < 0)
|
||||
return topology_id;
|
||||
|
||||
if (acpi_cpu_is_threaded(cpu)) {
|
||||
cpu_topology[cpu].thread_id = topology_id;
|
||||
topology_id = find_acpi_cpu_topology(cpu, 1);
|
||||
cpu_topology[cpu].core_id = topology_id;
|
||||
|
||||
/*
|
||||
* In the PPTT, CPUs below a node with the 'identical
|
||||
* implementation' flag have the same number of threads.
|
||||
* Count the number of threads for only one CPU (i.e.
|
||||
* one core_id) among those with the same hetero_id.
|
||||
* See the comment of find_acpi_cpu_topology_hetero_id()
|
||||
* for more details.
|
||||
*
|
||||
* One entry is created for each node having:
|
||||
* - the 'identical implementation' flag
|
||||
* - its parent not having the flag
|
||||
*/
|
||||
hetero_id = find_acpi_cpu_topology_hetero_id(cpu);
|
||||
entry = xa_load(&hetero_cpu, hetero_id);
|
||||
if (!entry) {
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
WARN_ON_ONCE(!entry);
|
||||
|
||||
if (entry) {
|
||||
entry->core_id = topology_id;
|
||||
entry->thread_num = 1;
|
||||
xa_store(&hetero_cpu, hetero_id,
|
||||
entry, GFP_KERNEL);
|
||||
}
|
||||
} else if (entry->core_id == topology_id) {
|
||||
entry->thread_num++;
|
||||
}
|
||||
} else {
|
||||
cpu_topology[cpu].thread_id = -1;
|
||||
cpu_topology[cpu].core_id = topology_id;
|
||||
}
|
||||
topology_id = find_acpi_cpu_topology_cluster(cpu);
|
||||
cpu_topology[cpu].cluster_id = topology_id;
|
||||
topology_id = find_acpi_cpu_topology_package(cpu);
|
||||
cpu_topology[cpu].package_id = topology_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a short loop since the number of XArray elements is the
|
||||
* number of heterogeneous CPU clusters. On a homogeneous system
|
||||
* there's only one entry in the XArray.
|
||||
*/
|
||||
xa_for_each(&hetero_cpu, hetero_id, entry) {
|
||||
max_smt_thread_num = max(max_smt_thread_num, entry->thread_num);
|
||||
xa_erase(&hetero_cpu, hetero_id);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);
|
||||
xa_destroy(&hetero_cpu);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM64_AMU_EXTN
|
||||
#define read_corecnt() read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0)
|
||||
#define read_constcnt() read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0)
|
||||
|
||||
@@ -823,12 +823,106 @@ void remove_cpu_topology(unsigned int cpu)
|
||||
clear_cpu_topology(cpu);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
|
||||
struct cpu_smt_info {
|
||||
unsigned int thread_num;
|
||||
int core_id;
|
||||
};
|
||||
|
||||
static bool __init acpi_cpu_is_threaded(int cpu)
|
||||
{
|
||||
int is_threaded = acpi_pptt_cpu_is_thread(cpu);
|
||||
|
||||
/*
|
||||
* if the PPTT doesn't have thread information, check for architecture
|
||||
* specific fallback if available
|
||||
*/
|
||||
if (is_threaded < 0)
|
||||
is_threaded = arch_cpu_is_threaded();
|
||||
|
||||
return !!is_threaded;
|
||||
}
|
||||
|
||||
/*
|
||||
* Propagate the topology information of the processor_topology_node tree to the
|
||||
* cpu_topology array.
|
||||
*/
|
||||
__weak int __init parse_acpi_topology(void)
|
||||
{
|
||||
unsigned int max_smt_thread_num = 1;
|
||||
struct cpu_smt_info *entry;
|
||||
struct xarray hetero_cpu;
|
||||
unsigned long hetero_id;
|
||||
int cpu, topology_id;
|
||||
|
||||
if (acpi_disabled)
|
||||
return 0;
|
||||
|
||||
xa_init(&hetero_cpu);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
topology_id = find_acpi_cpu_topology(cpu, 0);
|
||||
if (topology_id < 0)
|
||||
return topology_id;
|
||||
|
||||
if (acpi_cpu_is_threaded(cpu)) {
|
||||
cpu_topology[cpu].thread_id = topology_id;
|
||||
topology_id = find_acpi_cpu_topology(cpu, 1);
|
||||
cpu_topology[cpu].core_id = topology_id;
|
||||
|
||||
/*
|
||||
* In the PPTT, CPUs below a node with the 'identical
|
||||
* implementation' flag have the same number of threads.
|
||||
* Count the number of threads for only one CPU (i.e.
|
||||
* one core_id) among those with the same hetero_id.
|
||||
* See the comment of find_acpi_cpu_topology_hetero_id()
|
||||
* for more details.
|
||||
*
|
||||
* One entry is created for each node having:
|
||||
* - the 'identical implementation' flag
|
||||
* - its parent not having the flag
|
||||
*/
|
||||
hetero_id = find_acpi_cpu_topology_hetero_id(cpu);
|
||||
entry = xa_load(&hetero_cpu, hetero_id);
|
||||
if (!entry) {
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
WARN_ON_ONCE(!entry);
|
||||
|
||||
if (entry) {
|
||||
entry->core_id = topology_id;
|
||||
entry->thread_num = 1;
|
||||
xa_store(&hetero_cpu, hetero_id,
|
||||
entry, GFP_KERNEL);
|
||||
}
|
||||
} else if (entry->core_id == topology_id) {
|
||||
entry->thread_num++;
|
||||
}
|
||||
} else {
|
||||
cpu_topology[cpu].thread_id = -1;
|
||||
cpu_topology[cpu].core_id = topology_id;
|
||||
}
|
||||
topology_id = find_acpi_cpu_topology_cluster(cpu);
|
||||
cpu_topology[cpu].cluster_id = topology_id;
|
||||
topology_id = find_acpi_cpu_topology_package(cpu);
|
||||
cpu_topology[cpu].package_id = topology_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a short loop since the number of XArray elements is the
|
||||
* number of heterogeneous CPU clusters. On a homogeneous system
|
||||
* there's only one entry in the XArray.
|
||||
*/
|
||||
xa_for_each(&hetero_cpu, hetero_id, entry) {
|
||||
max_smt_thread_num = max(max_smt_thread_num, entry->thread_num);
|
||||
xa_erase(&hetero_cpu, hetero_id);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);
|
||||
xa_destroy(&hetero_cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
|
||||
void __init init_cpu_topology(void)
|
||||
{
|
||||
int cpu, ret;
|
||||
|
||||
@@ -85,6 +85,18 @@ struct driver_private {
|
||||
};
|
||||
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
|
||||
|
||||
#ifdef CONFIG_RUST
|
||||
/**
|
||||
* struct driver_type - Representation of a Rust driver type.
|
||||
*/
|
||||
struct driver_type {
|
||||
/**
|
||||
* @id: Representation of core::any::TypeId.
|
||||
*/
|
||||
u8 id[16];
|
||||
} __packed;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct device_private - structure to hold the private to the driver core portions of the device structure.
|
||||
*
|
||||
@@ -100,6 +112,7 @@ struct driver_private {
|
||||
* @async_driver - pointer to device driver awaiting probe via async_probe
|
||||
* @device - pointer back to the struct device that this structure is
|
||||
* associated with.
|
||||
* @driver_type - The type of the bound Rust driver.
|
||||
* @dead - This device is currently either in the process of or has been
|
||||
* removed from the system. Any asynchronous events scheduled for this
|
||||
* device should exit without taking any action.
|
||||
@@ -116,6 +129,9 @@ struct device_private {
|
||||
const struct device_driver *async_driver;
|
||||
char *deferred_probe_reason;
|
||||
struct device *device;
|
||||
#ifdef CONFIG_RUST
|
||||
struct driver_type driver_type;
|
||||
#endif
|
||||
u8 dead:1;
|
||||
};
|
||||
#define to_device_private_parent(obj) \
|
||||
|
||||
@@ -533,8 +533,7 @@ void bus_probe_device(struct device *dev)
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
if (sp->drivers_autoprobe)
|
||||
device_initial_probe(dev);
|
||||
device_initial_probe(dev);
|
||||
|
||||
mutex_lock(&sp->mutex);
|
||||
list_for_each_entry(sif, &sp->interfaces, node)
|
||||
|
||||
@@ -4138,7 +4138,7 @@ int __init devices_init(void)
|
||||
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
|
||||
if (!sysfs_dev_char_kobj)
|
||||
goto char_kobj_err;
|
||||
device_link_wq = alloc_workqueue("device_link_wq", 0, 0);
|
||||
device_link_wq = alloc_workqueue("device_link_wq", WQ_PERCPU, 0);
|
||||
if (!device_link_wq)
|
||||
goto wq_err;
|
||||
|
||||
|
||||
@@ -300,13 +300,30 @@ static ssize_t print_cpus_isolated(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
|
||||
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
static ssize_t print_cpus_nohz_full(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t housekeeping_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
|
||||
const struct cpumask *hk_mask;
|
||||
|
||||
hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE);
|
||||
|
||||
if (housekeeping_enabled(HK_TYPE_KERNEL_NOISE))
|
||||
return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(hk_mask));
|
||||
return sysfs_emit(buf, "\n");
|
||||
}
|
||||
static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
|
||||
static DEVICE_ATTR_RO(housekeeping);
|
||||
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
static ssize_t nohz_full_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
if (cpumask_available(tick_nohz_full_mask))
|
||||
return sysfs_emit(buf, "%*pbl\n",
|
||||
cpumask_pr_args(tick_nohz_full_mask));
|
||||
return sysfs_emit(buf, "\n");
|
||||
}
|
||||
static DEVICE_ATTR_RO(nohz_full);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CRASH_HOTPLUG
|
||||
@@ -505,6 +522,7 @@ static struct attribute *cpu_root_attrs[] = {
|
||||
&dev_attr_offline.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_isolated.attr,
|
||||
&dev_attr_housekeeping.attr,
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
&dev_attr_nohz_full.attr,
|
||||
#endif
|
||||
|
||||
@@ -193,7 +193,7 @@ void driver_deferred_probe_trigger(void)
|
||||
* Kick the re-probe thread. It may already be scheduled, but it is
|
||||
* safe to kick it again.
|
||||
*/
|
||||
queue_work(system_unbound_wq, &deferred_probe_work);
|
||||
queue_work(system_dfl_wq, &deferred_probe_work);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1077,7 +1077,15 @@ EXPORT_SYMBOL_GPL(device_attach);
|
||||
|
||||
void device_initial_probe(struct device *dev)
|
||||
{
|
||||
__device_attach(dev, true);
|
||||
struct subsys_private *sp = bus_to_subsys(dev->bus);
|
||||
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
if (sp->drivers_autoprobe)
|
||||
__device_attach(dev, true);
|
||||
|
||||
subsys_put(sp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1222,13 +1222,6 @@ static void devm_percpu_release(struct device *dev, void *pdata)
|
||||
free_percpu(p);
|
||||
}
|
||||
|
||||
static int devm_percpu_match(struct device *dev, void *data, void *p)
|
||||
{
|
||||
struct devres *devr = container_of(data, struct devres, data);
|
||||
|
||||
return *(void **)devr->data == p;
|
||||
}
|
||||
|
||||
/**
|
||||
* __devm_alloc_percpu - Resource-managed alloc_percpu
|
||||
* @dev: Device to allocate per-cpu memory for
|
||||
@@ -1264,21 +1257,3 @@ void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
|
||||
return pcpu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__devm_alloc_percpu);
|
||||
|
||||
/**
|
||||
* devm_free_percpu - Resource-managed free_percpu
|
||||
* @dev: Device this memory belongs to
|
||||
* @pdata: Per-cpu memory to free
|
||||
*
|
||||
* Free memory allocated with devm_alloc_percpu().
|
||||
*/
|
||||
void devm_free_percpu(struct device *dev, void __percpu *pdata)
|
||||
{
|
||||
/*
|
||||
* Use devres_release() to prevent memory leakage as
|
||||
* devm_free_pages() does.
|
||||
*/
|
||||
WARN_ON(devres_release(dev, devm_percpu_release, devm_percpu_match,
|
||||
(void *)(__force unsigned long)pdata));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_free_percpu);
|
||||
|
||||
@@ -47,7 +47,10 @@ static ssize_t timeout_show(const struct class *class, const struct class_attrib
|
||||
static ssize_t timeout_store(const struct class *class, const struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
|
||||
int tmp_loading_timeout;
|
||||
|
||||
if (kstrtoint(buf, 10, &tmp_loading_timeout))
|
||||
return -EINVAL;
|
||||
|
||||
if (tmp_loading_timeout < 0)
|
||||
tmp_loading_timeout = 0;
|
||||
@@ -157,7 +160,10 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
|
||||
struct fw_priv *fw_priv;
|
||||
ssize_t written = count;
|
||||
int loading = simple_strtol(buf, NULL, 10);
|
||||
int loading;
|
||||
|
||||
if (kstrtoint(buf, 10, &loading))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&fw_lock);
|
||||
fw_priv = fw_sysfs->fw_priv;
|
||||
|
||||
@@ -100,8 +100,10 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&fwlp->lock);
|
||||
if (fwlp->progress == FW_UPLOAD_PROG_IDLE)
|
||||
ret = -ENODEV;
|
||||
if (fwlp->progress == FW_UPLOAD_PROG_IDLE) {
|
||||
mutex_unlock(&fwlp->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
fwlp->ops->cancel(fwlp->fw_upload);
|
||||
mutex_unlock(&fwlp->lock);
|
||||
|
||||
@@ -207,9 +207,9 @@ impl platform::Driver for CPUFreqDTDriver {
|
||||
fn probe(
|
||||
pdev: &platform::Device<Core>,
|
||||
_id_info: Option<&Self::IdInfo>,
|
||||
) -> Result<Pin<KBox<Self>>> {
|
||||
) -> impl PinInit<Self, Error> {
|
||||
cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
|
||||
Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,13 +45,13 @@ impl auxiliary::Driver for NovaDriver {
|
||||
type IdInfo = ();
|
||||
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> {
|
||||
let data = try_pin_init!(NovaData { adev: adev.into() });
|
||||
|
||||
let drm = drm::Device::<Self>::new(adev.as_ref(), data)?;
|
||||
drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?;
|
||||
|
||||
Ok(KBox::new(Self { drm }, GFP_KERNEL)?.into())
|
||||
Ok(Self { drm })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ impl File {
|
||||
_file: &drm::File<File>,
|
||||
) -> Result<u32> {
|
||||
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 {
|
||||
|
||||
@@ -103,7 +103,7 @@ impl platform::Driver for TyrDriver {
|
||||
fn probe(
|
||||
pdev: &platform::Device<Core>,
|
||||
_info: Option<&Self::IdInfo>,
|
||||
) -> Result<Pin<KBox<Self>>> {
|
||||
) -> impl PinInit<Self, Error> {
|
||||
let core_clk = Clk::get(pdev.as_ref(), Some(c_str!("core")))?;
|
||||
let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("stacks")))?;
|
||||
let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("coregroup")))?;
|
||||
@@ -143,7 +143,7 @@ impl platform::Driver for TyrDriver {
|
||||
let tdev: ARef<TyrDevice> = drm::Device::new(pdev.as_ref(), data)?;
|
||||
drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?;
|
||||
|
||||
let driver = KBox::pin_init(try_pin_init!(TyrDriver { device: tdev }), GFP_KERNEL)?;
|
||||
let driver = TyrDriver { device: tdev };
|
||||
|
||||
// We need this to be dev_info!() because dev_dbg!() does not work at
|
||||
// all in Rust for now, and we need to see whether probe succeeded.
|
||||
|
||||
@@ -4,6 +4,7 @@ use kernel::{
|
||||
auxiliary,
|
||||
c_str,
|
||||
device::Core,
|
||||
devres::Devres,
|
||||
dma::Device,
|
||||
dma::DmaMask,
|
||||
pci,
|
||||
@@ -23,7 +24,8 @@ use crate::gpu::Gpu;
|
||||
pub(crate) struct NovaCore {
|
||||
#[pin]
|
||||
pub(crate) gpu: Gpu,
|
||||
_reg: auxiliary::Registration,
|
||||
#[pin]
|
||||
_reg: Devres<auxiliary::Registration>,
|
||||
}
|
||||
|
||||
const BAR0_SIZE: usize = SZ_16M;
|
||||
@@ -67,41 +69,33 @@ impl pci::Driver for NovaCore {
|
||||
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_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
|
||||
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
|
||||
pin_init::pin_init_scope(move || {
|
||||
dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
|
||||
|
||||
pdev.enable_device_mem()?;
|
||||
pdev.set_master();
|
||||
pdev.enable_device_mem()?;
|
||||
pdev.set_master();
|
||||
|
||||
// SAFETY: No concurrent DMA allocations or mappings can be made because
|
||||
// the device is still being probed and therefore isn't being used by
|
||||
// other threads of execution.
|
||||
unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
|
||||
// SAFETY: No concurrent DMA allocations or mappings can be made because
|
||||
// the device is still being probed and therefore isn't being used by
|
||||
// other threads of execution.
|
||||
unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
|
||||
|
||||
let devres_bar = Arc::pin_init(
|
||||
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
let bar = Arc::pin_init(
|
||||
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
|
||||
// Used to provided a `&Bar0` to `Gpu::new` without tying it to the lifetime of
|
||||
// `devres_bar`.
|
||||
let bar_clone = Arc::clone(&devres_bar);
|
||||
let bar = bar_clone.access(pdev.as_ref())?;
|
||||
|
||||
let this = KBox::pin_init(
|
||||
try_pin_init!(Self {
|
||||
gpu <- Gpu::new(pdev, devres_bar, bar),
|
||||
_reg: auxiliary::Registration::new(
|
||||
Ok(try_pin_init!(Self {
|
||||
gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?),
|
||||
_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
|
||||
)?,
|
||||
}),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
|
||||
Ok(this)
|
||||
),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
|
||||
|
||||
@@ -337,7 +337,7 @@ impl platform::Driver for Th1520PwmPlatformDriver {
|
||||
fn probe(
|
||||
pdev: &platform::Device<Core>,
|
||||
_id_info: Option<&Self::IdInfo>,
|
||||
) -> Result<Pin<KBox<Self>>> {
|
||||
) -> impl PinInit<Self, Error> {
|
||||
let dev = pdev.as_ref();
|
||||
let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
|
||||
|
||||
@@ -374,7 +374,7 @@ impl platform::Driver for Th1520PwmPlatformDriver {
|
||||
|
||||
pwm::Registration::register(dev, chip)?;
|
||||
|
||||
Ok(KBox::new(Th1520PwmPlatformDriver, GFP_KERNEL)?.into())
|
||||
Ok(Th1520PwmPlatformDriver)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
static unsigned int debugfs_allow __ro_after_init = DEFAULT_DEBUGFS_ALLOW_BITS;
|
||||
static bool debugfs_enabled __ro_after_init = IS_ENABLED(CONFIG_DEBUG_FS_ALLOW_ALL);
|
||||
|
||||
/*
|
||||
* Don't allow access attributes to be changed whilst the kernel is locked down
|
||||
@@ -287,9 +287,6 @@ static int debugfs_get_tree(struct fs_context *fc)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API))
|
||||
return -EPERM;
|
||||
|
||||
err = get_tree_single(fc, debugfs_fill_super);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -368,7 +365,7 @@ static struct dentry *debugfs_start_creating(const char *name,
|
||||
struct dentry *dentry;
|
||||
int error;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API))
|
||||
if (!debugfs_enabled)
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
if (!debugfs_initialized())
|
||||
@@ -883,21 +880,25 @@ static int __init debugfs_kernel(char *str)
|
||||
{
|
||||
if (str) {
|
||||
if (!strcmp(str, "on"))
|
||||
debugfs_allow = DEBUGFS_ALLOW_API | DEBUGFS_ALLOW_MOUNT;
|
||||
else if (!strcmp(str, "no-mount"))
|
||||
debugfs_allow = DEBUGFS_ALLOW_API;
|
||||
debugfs_enabled = true;
|
||||
else if (!strcmp(str, "off"))
|
||||
debugfs_allow = 0;
|
||||
debugfs_enabled = false;
|
||||
else if (!strcmp(str, "no-mount")) {
|
||||
pr_notice("debugfs=no-mount is a deprecated alias "
|
||||
"for debugfs=off\n");
|
||||
debugfs_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("debugfs", debugfs_kernel);
|
||||
|
||||
static int __init debugfs_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_MOUNT))
|
||||
if (!debugfs_enabled)
|
||||
return -EPERM;
|
||||
|
||||
retval = sysfs_create_mount_point(kernel_kobj, "debug");
|
||||
|
||||
@@ -55,17 +55,4 @@ enum {
|
||||
HAS_IOCTL = 16
|
||||
};
|
||||
|
||||
#define DEBUGFS_ALLOW_API BIT(0)
|
||||
#define DEBUGFS_ALLOW_MOUNT BIT(1)
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS_ALLOW_ALL
|
||||
#define DEFAULT_DEBUGFS_ALLOW_BITS (DEBUGFS_ALLOW_MOUNT | DEBUGFS_ALLOW_API)
|
||||
#endif
|
||||
#ifdef CONFIG_DEBUG_FS_DISALLOW_MOUNT
|
||||
#define DEFAULT_DEBUGFS_ALLOW_BITS (DEBUGFS_ALLOW_API)
|
||||
#endif
|
||||
#ifdef CONFIG_DEBUG_FS_ALLOW_NONE
|
||||
#define DEFAULT_DEBUGFS_ALLOW_BITS (0)
|
||||
#endif
|
||||
|
||||
#endif /* _DEBUGFS_INTERNAL_H_ */
|
||||
|
||||
@@ -675,11 +675,14 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
|
||||
if (parent) {
|
||||
ret = security_kernfs_init_security(parent, kn);
|
||||
if (ret)
|
||||
goto err_out3;
|
||||
goto err_out4;
|
||||
}
|
||||
|
||||
return kn;
|
||||
|
||||
err_out4:
|
||||
simple_xattrs_free(&kn->iattr->xattrs, NULL);
|
||||
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
||||
err_out3:
|
||||
spin_lock(&root->kernfs_idr_lock);
|
||||
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
|
||||
|
||||
@@ -298,6 +298,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
|
||||
if (info->root->flags & KERNFS_ROOT_SUPPORT_EXPORTOP)
|
||||
sb->s_export_op = &kernfs_export_ops;
|
||||
sb->s_time_gran = 1;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
|
||||
/* sysfs dentries and inodes don't require IO to create */
|
||||
sb->s_shrink->seeks = 0;
|
||||
|
||||
@@ -36,6 +36,9 @@ static umode_t __first_visible(const struct attribute_group *grp, struct kobject
|
||||
if (grp->attrs && grp->attrs[0] && grp->is_visible)
|
||||
return grp->is_visible(kobj, grp->attrs[0], 0);
|
||||
|
||||
if (grp->attrs && grp->attrs[0] && grp->is_visible_const)
|
||||
return grp->is_visible_const(kobj, grp->attrs[0], 0);
|
||||
|
||||
if (grp->bin_attrs && grp->bin_attrs[0] && grp->is_bin_visible)
|
||||
return grp->is_bin_visible(kobj, grp->bin_attrs[0], 0);
|
||||
|
||||
@@ -61,8 +64,11 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
|
||||
*/
|
||||
if (update)
|
||||
kernfs_remove_by_name(parent, (*attr)->name);
|
||||
if (grp->is_visible) {
|
||||
mode = grp->is_visible(kobj, *attr, i);
|
||||
if (grp->is_visible || grp->is_visible_const) {
|
||||
if (grp->is_visible)
|
||||
mode = grp->is_visible(kobj, *attr, i);
|
||||
else
|
||||
mode = grp->is_visible_const(kobj, *attr, i);
|
||||
mode &= ~SYSFS_GROUP_INVISIBLE;
|
||||
if (!mode)
|
||||
continue;
|
||||
|
||||
@@ -80,6 +80,11 @@ extern struct cpu_topology cpu_topology[NR_CPUS];
|
||||
#define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
|
||||
#define topology_cluster_cpumask(cpu) (&cpu_topology[cpu].cluster_sibling)
|
||||
#define topology_llc_cpumask(cpu) (&cpu_topology[cpu].llc_sibling)
|
||||
|
||||
#ifndef arch_cpu_is_threaded
|
||||
#define arch_cpu_is_threaded() (0)
|
||||
#endif
|
||||
|
||||
void init_cpu_topology(void);
|
||||
void store_cpu_topology(unsigned int cpuid);
|
||||
const struct cpumask *cpu_coregroup_mask(int cpu);
|
||||
|
||||
@@ -281,25 +281,6 @@ int __must_check device_create_bin_file(struct device *dev,
|
||||
void device_remove_bin_file(struct device *dev,
|
||||
const struct bin_attribute *attr);
|
||||
|
||||
/**
|
||||
* devm_alloc_percpu - Resource-managed alloc_percpu
|
||||
* @dev: Device to allocate per-cpu memory for
|
||||
* @type: Type to allocate per-cpu memory for
|
||||
*
|
||||
* Managed alloc_percpu. Per-cpu memory allocated with this function is
|
||||
* automatically freed on driver detach.
|
||||
*
|
||||
* RETURNS:
|
||||
* Pointer to allocated memory on success, NULL on failure.
|
||||
*/
|
||||
#define devm_alloc_percpu(dev, type) \
|
||||
((typeof(type) __percpu *)__devm_alloc_percpu((dev), sizeof(type), \
|
||||
__alignof__(type)))
|
||||
|
||||
void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
|
||||
size_t align);
|
||||
void devm_free_percpu(struct device *dev, void __percpu *pdata);
|
||||
|
||||
struct device_dma_parameters {
|
||||
/*
|
||||
* a low level driver may set these to teach IOMMU code about
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/stdarg.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/bug.h>
|
||||
#include <asm/percpu.h>
|
||||
|
||||
struct device;
|
||||
struct device_node;
|
||||
@@ -96,6 +97,22 @@ devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt, va_list ap);
|
||||
char * __printf(3, 4) __malloc
|
||||
devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* devm_alloc_percpu - Resource-managed alloc_percpu
|
||||
* @dev: Device to allocate per-cpu memory for
|
||||
* @type: Type to allocate per-cpu memory for
|
||||
*
|
||||
* Managed alloc_percpu. Per-cpu memory allocated with this function is
|
||||
* automatically freed on driver detach.
|
||||
*
|
||||
* RETURNS:
|
||||
* Pointer to allocated memory on success, NULL on failure.
|
||||
*/
|
||||
#define devm_alloc_percpu(dev, type) \
|
||||
((typeof(type) __percpu *)__devm_alloc_percpu((dev), sizeof(type), __alignof__(type)))
|
||||
|
||||
void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, size_t align);
|
||||
|
||||
unsigned long devm_get_free_pages(struct device *dev, gfp_t gfp_mask, unsigned int order);
|
||||
void devm_free_pages(struct device *dev, unsigned long addr);
|
||||
|
||||
|
||||
@@ -867,7 +867,7 @@ struct mhi_device_id {
|
||||
kernel_ulong_t driver_data;
|
||||
};
|
||||
|
||||
#define AUXILIARY_NAME_SIZE 32
|
||||
#define AUXILIARY_NAME_SIZE 40
|
||||
#define AUXILIARY_MODULE_PREFIX "auxiliary:"
|
||||
|
||||
struct auxiliary_device_id {
|
||||
|
||||
@@ -80,7 +80,7 @@ static inline void __iomem *
|
||||
devm_platform_get_and_ioremap_resource(struct platform_device *pdev,
|
||||
unsigned int index, struct resource **res)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,14 +88,14 @@ static inline void __iomem *
|
||||
devm_platform_ioremap_resource(struct platform_device *pdev,
|
||||
unsigned int index)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline void __iomem *
|
||||
devm_platform_ioremap_resource_byname(struct platform_device *pdev,
|
||||
const char *name)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,12 @@ do { \
|
||||
#define sysfs_attr_init(attr) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CFI
|
||||
#define __SYSFS_FUNCTION_ALTERNATIVE(MEMBERS...) struct { MEMBERS }
|
||||
#else
|
||||
#define __SYSFS_FUNCTION_ALTERNATIVE(MEMBERS...) union { MEMBERS }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct attribute_group - data structure used to declare an attribute group.
|
||||
* @name: Optional: Attribute group name
|
||||
@@ -98,14 +104,21 @@ do { \
|
||||
*/
|
||||
struct attribute_group {
|
||||
const char *name;
|
||||
umode_t (*is_visible)(struct kobject *,
|
||||
struct attribute *, int);
|
||||
__SYSFS_FUNCTION_ALTERNATIVE(
|
||||
umode_t (*is_visible)(struct kobject *,
|
||||
struct attribute *, int);
|
||||
umode_t (*is_visible_const)(struct kobject *,
|
||||
const struct attribute *, int);
|
||||
);
|
||||
umode_t (*is_bin_visible)(struct kobject *,
|
||||
const struct bin_attribute *, int);
|
||||
size_t (*bin_size)(struct kobject *,
|
||||
const struct bin_attribute *,
|
||||
int);
|
||||
struct attribute **attrs;
|
||||
union {
|
||||
struct attribute **attrs;
|
||||
const struct attribute *const *attrs_const;
|
||||
};
|
||||
const struct bin_attribute *const *bin_attrs;
|
||||
};
|
||||
|
||||
@@ -238,28 +251,20 @@ struct attribute_group {
|
||||
.store = _store, \
|
||||
}
|
||||
|
||||
#define __ATTR_RO(_name) { \
|
||||
.attr = { .name = __stringify(_name), .mode = 0444 }, \
|
||||
.show = _name##_show, \
|
||||
}
|
||||
|
||||
#define __ATTR_RO_MODE(_name, _mode) { \
|
||||
.attr = { .name = __stringify(_name), \
|
||||
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
|
||||
.show = _name##_show, \
|
||||
}
|
||||
|
||||
#define __ATTR_RW_MODE(_name, _mode) { \
|
||||
.attr = { .name = __stringify(_name), \
|
||||
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
|
||||
.show = _name##_show, \
|
||||
.store = _name##_store, \
|
||||
}
|
||||
#define __ATTR_RO(_name) \
|
||||
__ATTR_RO_MODE(_name, 0444)
|
||||
|
||||
#define __ATTR_WO(_name) { \
|
||||
.attr = { .name = __stringify(_name), .mode = 0200 }, \
|
||||
.store = _name##_store, \
|
||||
}
|
||||
#define __ATTR_RW_MODE(_name, _mode) \
|
||||
__ATTR(_name, _mode, _name##_show, _name##_store)
|
||||
|
||||
#define __ATTR_WO(_name) \
|
||||
__ATTR(_name, 0200, NULL, _name##_store)
|
||||
|
||||
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
|
||||
|
||||
@@ -284,7 +289,12 @@ static const struct attribute_group *_name##_groups[] = { \
|
||||
|
||||
#define ATTRIBUTE_GROUPS(_name) \
|
||||
static const struct attribute_group _name##_group = { \
|
||||
.attrs = _name##_attrs, \
|
||||
.attrs = _Generic(_name##_attrs, \
|
||||
struct attribute **: \
|
||||
_name##_attrs, \
|
||||
const struct attribute *const *: \
|
||||
(void *)_name##_attrs \
|
||||
), \
|
||||
}; \
|
||||
__ATTRIBUTE_GROUPS(_name)
|
||||
|
||||
|
||||
@@ -688,7 +688,7 @@ choice
|
||||
help
|
||||
This selects the default access restrictions for debugfs.
|
||||
It can be overridden with kernel command line option
|
||||
debugfs=[on,no-mount,off]. The restrictions apply for API access
|
||||
debugfs=[on,off]. The restrictions apply for API access
|
||||
and filesystem registration.
|
||||
|
||||
config DEBUG_FS_ALLOW_ALL
|
||||
@@ -697,13 +697,6 @@ config DEBUG_FS_ALLOW_ALL
|
||||
No restrictions apply. Both API and filesystem registration
|
||||
is on. This is the normal default operation.
|
||||
|
||||
config DEBUG_FS_DISALLOW_MOUNT
|
||||
bool "Do not register debugfs as filesystem"
|
||||
help
|
||||
The API is open but filesystem is not loaded. Clients can still do
|
||||
their work and read with debug tools that do not need
|
||||
debugfs filesystem.
|
||||
|
||||
config DEBUG_FS_ALLOW_NONE
|
||||
bool "No access"
|
||||
help
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/jump_label.h>
|
||||
@@ -86,6 +87,12 @@
|
||||
#include <linux/xarray.h>
|
||||
#include <trace/events/rust_sample.h>
|
||||
|
||||
/*
|
||||
* The driver-core Rust code needs to know about some C driver-core private
|
||||
* structures.
|
||||
*/
|
||||
#include <../../drivers/base/base.h>
|
||||
|
||||
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
|
||||
// Used by `#[export]` in `drivers/gpu/drm/drm_panic_qr.rs`.
|
||||
#include <drm/drm_panic.h>
|
||||
|
||||
@@ -23,9 +23,21 @@ bool rust_helper_dev_is_pci(const struct device *dev)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PCI_MSI
|
||||
int rust_helper_pci_alloc_irq_vectors(struct pci_dev *dev,
|
||||
unsigned int min_vecs,
|
||||
unsigned int max_vecs,
|
||||
unsigned int flags)
|
||||
{
|
||||
return pci_alloc_irq_vectors(dev, min_vecs, max_vecs, flags);
|
||||
}
|
||||
|
||||
void rust_helper_pci_free_irq_vectors(struct pci_dev *dev)
|
||||
{
|
||||
pci_free_irq_vectors(dev);
|
||||
}
|
||||
|
||||
int rust_helper_pci_irq_vector(struct pci_dev *pdev, unsigned int nvec)
|
||||
{
|
||||
return pci_irq_vector(pdev, nvec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -33,3 +33,8 @@ s64 rust_helper_ktime_to_ms(const ktime_t kt)
|
||||
{
|
||||
return ktime_to_ms(kt);
|
||||
}
|
||||
|
||||
void rust_helper_udelay(unsigned long usec)
|
||||
{
|
||||
udelay(usec);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
use crate::{
|
||||
bindings, container_of, device,
|
||||
device_id::{RawDeviceId, RawDeviceIdIndex},
|
||||
devres::Devres,
|
||||
driver,
|
||||
error::{from_result, to_result, Result},
|
||||
prelude::*,
|
||||
@@ -15,6 +16,7 @@ use crate::{
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::offset_of,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
@@ -68,9 +70,9 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
let info = T::ID_TABLE.info(id.index());
|
||||
|
||||
from_result(|| {
|
||||
let data = T::probe(adev, info)?;
|
||||
let data = T::probe(adev, info);
|
||||
|
||||
adev.as_ref().set_drvdata(data);
|
||||
adev.as_ref().set_drvdata(data)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
@@ -85,7 +87,7 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
// SAFETY: `remove_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
drop(unsafe { adev.as_ref().drvdata_obtain::<Pin<KBox<T>>>() });
|
||||
drop(unsafe { adev.as_ref().drvdata_obtain::<T>() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +186,7 @@ pub trait Driver {
|
||||
/// Auxiliary driver probe.
|
||||
///
|
||||
/// Called when an auxiliary device is matches a corresponding driver.
|
||||
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
|
||||
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
|
||||
}
|
||||
|
||||
/// The auxiliary device representation.
|
||||
@@ -214,14 +216,25 @@ impl<Ctx: device::DeviceContext> Device<Ctx> {
|
||||
// `struct auxiliary_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()
|
||||
impl Device<device::Bound> {
|
||||
/// Returns a bound reference to the parent [`device::Device`].
|
||||
pub fn parent(&self) -> &device::Device<device::Bound> {
|
||||
let parent = (**self).parent();
|
||||
|
||||
// SAFETY: A bound auxiliary device always has a bound parent device.
|
||||
unsafe { parent.as_bound() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// 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() }
|
||||
}
|
||||
|
||||
extern "C" fn release(dev: *mut bindings::device) {
|
||||
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
|
||||
// embedded in `struct auxiliary_device`.
|
||||
@@ -233,6 +246,12 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `auxiliary::Device` is a transparent wrapper of `struct auxiliary_device`.
|
||||
// The offset is guaranteed to point to a valid device field inside `auxiliary::Device`.
|
||||
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
|
||||
const OFFSET: usize = offset_of!(bindings::auxiliary_device, dev);
|
||||
}
|
||||
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
@@ -278,8 +297,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
|
||||
///
|
||||
@@ -289,44 +308,55 @@ pub struct Registration(NonNull<bindings::auxiliary_device>);
|
||||
|
||||
impl Registration {
|
||||
/// Create and register a new auxiliary device.
|
||||
pub fn new(parent: &device::Device, name: &CStr, id: u32, modname: &CStr) -> Result<Self> {
|
||||
let boxed = KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)?;
|
||||
let adev = boxed.get();
|
||||
pub fn new<'a>(
|
||||
parent: &'a device::Device<device::Bound>,
|
||||
name: &'a CStr,
|
||||
id: u32,
|
||||
modname: &'a CStr,
|
||||
) -> impl PinInit<Devres<Self>, Error> + 'a {
|
||||
pin_init::pin_init_scope(move || {
|
||||
let boxed = KBox::new(Opaque::<bindings::auxiliary_device>::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) };
|
||||
// which has not been initialized yet.
|
||||
unsafe { bindings::auxiliary_device_init(adev) };
|
||||
|
||||
return Err(Error::from_errno(ret));
|
||||
}
|
||||
// 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 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) }))
|
||||
// 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));
|
||||
}
|
||||
|
||||
// INVARIANT: The device will remain registered until `auxiliary_device_delete()` is
|
||||
// called, which happens in `Self::drop()`.
|
||||
Ok(Devres::new(
|
||||
parent,
|
||||
// SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated
|
||||
// successfully.
|
||||
Self(unsafe { NonNull::new_unchecked(adev) }),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -893,9 +893,9 @@ pub trait Driver {
|
||||
/// fn probe(
|
||||
/// pdev: &platform::Device<Core>,
|
||||
/// _id_info: Option<&Self::IdInfo>,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// cpufreq::Registration::<SampleDriver>::new_foreign_owned(pdev.as_ref())?;
|
||||
/// Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
|
||||
/// Ok(Self {})
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@@ -21,12 +21,15 @@ use core::mem::ManuallyDrop;
|
||||
use core::ops::Deref;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{Reader, Writer};
|
||||
pub use traits::{BinaryReader, BinaryReaderMut, BinaryWriter, Reader, Writer};
|
||||
|
||||
mod callback_adapters;
|
||||
use callback_adapters::{FormatAdapter, NoWriter, WritableAdapter};
|
||||
mod file_ops;
|
||||
use file_ops::{FileOps, ReadFile, ReadWriteFile, WriteFile};
|
||||
use file_ops::{
|
||||
BinaryReadFile, BinaryReadWriteFile, BinaryWriteFile, FileOps, ReadFile, ReadWriteFile,
|
||||
WriteFile,
|
||||
};
|
||||
#[cfg(CONFIG_DEBUG_FS)]
|
||||
mod entry;
|
||||
#[cfg(CONFIG_DEBUG_FS)]
|
||||
@@ -150,6 +153,32 @@ impl Dir {
|
||||
self.create_file(name, data, file_ops)
|
||||
}
|
||||
|
||||
/// Creates a read-only binary file in this directory.
|
||||
///
|
||||
/// The file's contents are produced by invoking [`BinaryWriter::write_to_slice`] on the value
|
||||
/// initialized by `data`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use kernel::c_str;
|
||||
/// # use kernel::debugfs::Dir;
|
||||
/// # use kernel::prelude::*;
|
||||
/// # let dir = Dir::new(c_str!("my_debugfs_dir"));
|
||||
/// let file = KBox::pin_init(dir.read_binary_file(c_str!("foo"), [0x1, 0x2]), GFP_KERNEL)?;
|
||||
/// # Ok::<(), Error>(())
|
||||
/// ```
|
||||
pub fn read_binary_file<'a, T, E: 'a>(
|
||||
&'a self,
|
||||
name: &'a CStr,
|
||||
data: impl PinInit<T, E> + 'a,
|
||||
) -> impl PinInit<File<T>, E> + 'a
|
||||
where
|
||||
T: BinaryWriter + Send + Sync + 'static,
|
||||
{
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a read-only file in this directory, with contents from a callback.
|
||||
///
|
||||
/// `f` must be a function item or a non-capturing closure.
|
||||
@@ -206,6 +235,22 @@ impl Dir {
|
||||
self.create_file(name, data, file_ops)
|
||||
}
|
||||
|
||||
/// Creates a read-write binary file in this directory.
|
||||
///
|
||||
/// Reading the file uses the [`BinaryWriter`] implementation.
|
||||
/// Writing to the file uses the [`BinaryReader`] implementation.
|
||||
pub fn read_write_binary_file<'a, T, E: 'a>(
|
||||
&'a self,
|
||||
name: &'a CStr,
|
||||
data: impl PinInit<T, E> + 'a,
|
||||
) -> impl PinInit<File<T>, E> + 'a
|
||||
where
|
||||
T: BinaryWriter + BinaryReader + Send + Sync + 'static,
|
||||
{
|
||||
let file_ops = &<T as BinaryReadWriteFile<_>>::FILE_OPS;
|
||||
self.create_file(name, data, file_ops)
|
||||
}
|
||||
|
||||
/// Creates a read-write file in this directory, with logic from callbacks.
|
||||
///
|
||||
/// Reading from the file is handled by `f`. Writing to the file is handled by `w`.
|
||||
@@ -248,6 +293,23 @@ impl Dir {
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a write-only binary file in this directory.
|
||||
///
|
||||
/// The file owns its backing data. Writing to the file uses the [`BinaryReader`]
|
||||
/// implementation.
|
||||
///
|
||||
/// The file is removed when the returned [`File`] is dropped.
|
||||
pub fn write_binary_file<'a, T, E: 'a>(
|
||||
&'a self,
|
||||
name: &'a CStr,
|
||||
data: impl PinInit<T, E> + 'a,
|
||||
) -> impl PinInit<File<T>, E> + 'a
|
||||
where
|
||||
T: BinaryReader + Send + Sync + 'static,
|
||||
{
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a write-only file in this directory, with write logic from a callback.
|
||||
///
|
||||
/// `w` must be a function item or a non-capturing closure.
|
||||
@@ -468,6 +530,20 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a read-only binary file in this directory.
|
||||
///
|
||||
/// The file's contents are produced by invoking [`BinaryWriter::write_to_slice`].
|
||||
///
|
||||
/// This function does not produce an owning handle to the file. The created file is removed
|
||||
/// when the [`Scope`] that this directory belongs to is dropped.
|
||||
pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>(
|
||||
&self,
|
||||
name: &CStr,
|
||||
data: &'data T,
|
||||
) {
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a read-only file in this directory, with contents from a callback.
|
||||
///
|
||||
/// The file contents are generated by calling `f` with `data`.
|
||||
@@ -505,6 +581,22 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
|
||||
self.create_file(name, data, vtable)
|
||||
}
|
||||
|
||||
/// Creates a read-write binary file in this directory.
|
||||
///
|
||||
/// Reading the file uses the [`BinaryWriter`] implementation on `data`. Writing to the file
|
||||
/// uses the [`BinaryReader`] implementation on `data`.
|
||||
///
|
||||
/// This function does not produce an owning handle to the file. The created file is removed
|
||||
/// when the [`Scope`] that this directory belongs to is dropped.
|
||||
pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'static>(
|
||||
&self,
|
||||
name: &CStr,
|
||||
data: &'data T,
|
||||
) {
|
||||
let vtable = &<T as BinaryReadWriteFile<_>>::FILE_OPS;
|
||||
self.create_file(name, data, vtable)
|
||||
}
|
||||
|
||||
/// Creates a read-write file in this directory, with logic from callbacks.
|
||||
///
|
||||
/// Reading from the file is handled by `f`. Writing to the file is handled by `w`.
|
||||
@@ -544,6 +636,20 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
|
||||
self.create_file(name, data, vtable)
|
||||
}
|
||||
|
||||
/// Creates a write-only binary file in this directory.
|
||||
///
|
||||
/// Writing to the file uses the [`BinaryReader`] implementation on `data`.
|
||||
///
|
||||
/// This function does not produce an owning handle to the file. The created file is removed
|
||||
/// when the [`Scope`] that this directory belongs to is dropped.
|
||||
pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>(
|
||||
&self,
|
||||
name: &CStr,
|
||||
data: &'data T,
|
||||
) {
|
||||
self.create_file(name, data, &T::FILE_OPS)
|
||||
}
|
||||
|
||||
/// Creates a write-only file in this directory, with write logic from a callback.
|
||||
///
|
||||
/// Writing to the file is handled by `w`.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2025 Google LLC.
|
||||
|
||||
use super::{Reader, Writer};
|
||||
use super::{BinaryReader, BinaryWriter, Reader, Writer};
|
||||
use crate::debugfs::callback_adapters::Adapter;
|
||||
use crate::fmt;
|
||||
use crate::fs::file;
|
||||
use crate::prelude::*;
|
||||
use crate::seq_file::SeqFile;
|
||||
use crate::seq_print;
|
||||
@@ -245,3 +246,140 @@ impl<T: Reader + Sync> WriteFile<T> for T {
|
||||
unsafe { FileOps::new(operations, 0o200) }
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" fn blob_read<T: BinaryWriter>(
|
||||
file: *mut bindings::file,
|
||||
buf: *mut c_char,
|
||||
count: usize,
|
||||
ppos: *mut bindings::loff_t,
|
||||
) -> isize {
|
||||
// SAFETY:
|
||||
// - `file` is a valid pointer to a `struct file`.
|
||||
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
|
||||
let this = unsafe { &*((*file).private_data.cast::<T>()) };
|
||||
|
||||
// SAFETY:
|
||||
// - `ppos` is a valid `file::Offset` pointer.
|
||||
// - We have exclusive access to `ppos`.
|
||||
let pos: &mut file::Offset = unsafe { &mut *ppos };
|
||||
|
||||
let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer();
|
||||
|
||||
let ret = || -> Result<isize> {
|
||||
let written = this.write_to_slice(&mut writer, pos)?;
|
||||
|
||||
Ok(written.try_into()?)
|
||||
}();
|
||||
|
||||
match ret {
|
||||
Ok(n) => n,
|
||||
Err(e) => e.to_errno() as isize,
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of [`FileOps`] for read only binary files.
|
||||
pub(crate) trait BinaryReadFile<T> {
|
||||
const FILE_OPS: FileOps<T>;
|
||||
}
|
||||
|
||||
impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
|
||||
const FILE_OPS: FileOps<T> = {
|
||||
let operations = bindings::file_operations {
|
||||
read: Some(blob_read::<T>),
|
||||
llseek: Some(bindings::default_llseek),
|
||||
open: Some(bindings::simple_open),
|
||||
// SAFETY: `file_operations` supports zeroes in all fields.
|
||||
..unsafe { core::mem::zeroed() }
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
|
||||
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
|
||||
// corresponding `struct file`.
|
||||
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
|
||||
// - `default_llseek()` does not access the `struct file`'s private data.
|
||||
unsafe { FileOps::new(operations, 0o400) }
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" fn blob_write<T: BinaryReader>(
|
||||
file: *mut bindings::file,
|
||||
buf: *const c_char,
|
||||
count: usize,
|
||||
ppos: *mut bindings::loff_t,
|
||||
) -> isize {
|
||||
// SAFETY:
|
||||
// - `file` is a valid pointer to a `struct file`.
|
||||
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
|
||||
let this = unsafe { &*((*file).private_data.cast::<T>()) };
|
||||
|
||||
// SAFETY:
|
||||
// - `ppos` is a valid `file::Offset` pointer.
|
||||
// - We have exclusive access to `ppos`.
|
||||
let pos: &mut file::Offset = unsafe { &mut *ppos };
|
||||
|
||||
let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader();
|
||||
|
||||
let ret = || -> Result<isize> {
|
||||
let read = this.read_from_slice(&mut reader, pos)?;
|
||||
|
||||
Ok(read.try_into()?)
|
||||
}();
|
||||
|
||||
match ret {
|
||||
Ok(n) => n,
|
||||
Err(e) => e.to_errno() as isize,
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of [`FileOps`] for write only binary files.
|
||||
pub(crate) trait BinaryWriteFile<T> {
|
||||
const FILE_OPS: FileOps<T>;
|
||||
}
|
||||
|
||||
impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
|
||||
const FILE_OPS: FileOps<T> = {
|
||||
let operations = bindings::file_operations {
|
||||
write: Some(blob_write::<T>),
|
||||
llseek: Some(bindings::default_llseek),
|
||||
open: Some(bindings::simple_open),
|
||||
// SAFETY: `file_operations` supports zeroes in all fields.
|
||||
..unsafe { core::mem::zeroed() }
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
|
||||
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
|
||||
// corresponding `struct file`.
|
||||
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
|
||||
// - `default_llseek()` does not access the `struct file`'s private data.
|
||||
unsafe { FileOps::new(operations, 0o200) }
|
||||
};
|
||||
}
|
||||
|
||||
/// Representation of [`FileOps`] for read/write binary files.
|
||||
pub(crate) trait BinaryReadWriteFile<T> {
|
||||
const FILE_OPS: FileOps<T>;
|
||||
}
|
||||
|
||||
impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
|
||||
const FILE_OPS: FileOps<T> = {
|
||||
let operations = bindings::file_operations {
|
||||
read: Some(blob_read::<T>),
|
||||
write: Some(blob_write::<T>),
|
||||
llseek: Some(bindings::default_llseek),
|
||||
open: Some(bindings::simple_open),
|
||||
// SAFETY: `file_operations` supports zeroes in all fields.
|
||||
..unsafe { core::mem::zeroed() }
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
|
||||
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
|
||||
// corresponding `struct file`.
|
||||
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
|
||||
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
|
||||
// - `default_llseek()` does not access the `struct file`'s private data.
|
||||
unsafe { FileOps::new(operations, 0o600) }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
|
||||
//! Traits for rendering or updating values exported to DebugFS.
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use crate::fmt;
|
||||
use crate::fs::file;
|
||||
use crate::prelude::*;
|
||||
use crate::sync::atomic::{Atomic, AtomicBasicOps, AtomicType, Relaxed};
|
||||
use crate::sync::Arc;
|
||||
use crate::sync::Mutex;
|
||||
use crate::uaccess::UserSliceReader;
|
||||
use crate::transmute::{AsBytes, FromBytes};
|
||||
use crate::uaccess::{UserSliceReader, UserSliceWriter};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::str::FromStr;
|
||||
|
||||
/// A trait for types that can be written into a string.
|
||||
@@ -36,6 +41,110 @@ impl<T: fmt::Debug> Writer for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be written out as binary.
|
||||
pub trait BinaryWriter {
|
||||
/// Writes the binary form of `self` into `writer`.
|
||||
///
|
||||
/// `offset` is the requested offset into the binary representation of `self`.
|
||||
///
|
||||
/// On success, returns the number of bytes written in to `writer`.
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize>;
|
||||
}
|
||||
|
||||
// Base implementation for any `T: AsBytes`.
|
||||
impl<T: AsBytes> BinaryWriter for T {
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
writer.write_slice_file(self.as_bytes(), offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Mutex<T>`: Support a `T` with an outer mutex.
|
||||
impl<T: BinaryWriter> BinaryWriter for Mutex<T> {
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
let guard = self.lock();
|
||||
|
||||
guard.write_to_slice(writer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with no lock or an inner lock.
|
||||
impl<T, A> BinaryWriter for Box<T, A>
|
||||
where
|
||||
T: BinaryWriter,
|
||||
A: Allocator,
|
||||
{
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().write_to_slice(writer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with no lock or an inner lock.
|
||||
impl<T, A> BinaryWriter for Pin<Box<T, A>>
|
||||
where
|
||||
T: BinaryWriter,
|
||||
A: Allocator,
|
||||
{
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().write_to_slice(writer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Arc<T>`: Support a `Arc<T>` with no lock or an inner lock.
|
||||
impl<T> BinaryWriter for Arc<T>
|
||||
where
|
||||
T: BinaryWriter,
|
||||
{
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().write_to_slice(writer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Vec<T, A>`.
|
||||
impl<T, A> BinaryWriter for Vec<T, A>
|
||||
where
|
||||
T: AsBytes,
|
||||
A: Allocator,
|
||||
{
|
||||
fn write_to_slice(
|
||||
&self,
|
||||
writer: &mut UserSliceWriter,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
let slice = self.as_slice();
|
||||
|
||||
// SAFETY: `T: AsBytes` allows us to treat `&[T]` as `&[u8]`.
|
||||
let buffer = unsafe {
|
||||
core::slice::from_raw_parts(slice.as_ptr().cast(), core::mem::size_of_val(slice))
|
||||
};
|
||||
|
||||
writer.write_slice_file(buffer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be updated from a user slice.
|
||||
///
|
||||
/// This works similarly to `FromStr`, but operates on a `UserSliceReader` rather than a &str.
|
||||
@@ -81,3 +190,130 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be constructed from a binary representation.
|
||||
///
|
||||
/// See also [`BinaryReader`] for interior mutability.
|
||||
pub trait BinaryReaderMut {
|
||||
/// Reads the binary form of `self` from `reader`.
|
||||
///
|
||||
/// Same as [`BinaryReader::read_from_slice`], but takes a mutable reference.
|
||||
///
|
||||
/// `offset` is the requested offset into the binary representation of `self`.
|
||||
///
|
||||
/// On success, returns the number of bytes read from `reader`.
|
||||
fn read_from_slice_mut(
|
||||
&mut self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize>;
|
||||
}
|
||||
|
||||
// Base implementation for any `T: AsBytes + FromBytes`.
|
||||
impl<T: AsBytes + FromBytes> BinaryReaderMut for T {
|
||||
fn read_from_slice_mut(
|
||||
&mut self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
reader.read_slice_file(self.as_bytes_mut(), offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an outer lock.
|
||||
impl<T: ?Sized + BinaryReaderMut, A: Allocator> BinaryReaderMut for Box<T, A> {
|
||||
fn read_from_slice_mut(
|
||||
&mut self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref_mut().read_from_slice_mut(reader, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Vec<T, A>`: Support a `Vec<T, A>` with an outer lock.
|
||||
impl<T, A> BinaryReaderMut for Vec<T, A>
|
||||
where
|
||||
T: AsBytes + FromBytes,
|
||||
A: Allocator,
|
||||
{
|
||||
fn read_from_slice_mut(
|
||||
&mut self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
let slice = self.as_mut_slice();
|
||||
|
||||
// SAFETY: `T: AsBytes + FromBytes` allows us to treat `&mut [T]` as `&mut [u8]`.
|
||||
let buffer = unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
slice.as_mut_ptr().cast(),
|
||||
core::mem::size_of_val(slice),
|
||||
)
|
||||
};
|
||||
|
||||
reader.read_slice_file(buffer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be constructed from a binary representation.
|
||||
///
|
||||
/// See also [`BinaryReaderMut`] for the mutable version.
|
||||
pub trait BinaryReader {
|
||||
/// Reads the binary form of `self` from `reader`.
|
||||
///
|
||||
/// `offset` is the requested offset into the binary representation of `self`.
|
||||
///
|
||||
/// On success, returns the number of bytes read from `reader`.
|
||||
fn read_from_slice(
|
||||
&self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize>;
|
||||
}
|
||||
|
||||
// Delegate for `Mutex<T>`: Support a `T` with an outer `Mutex`.
|
||||
impl<T: BinaryReaderMut + Unpin> BinaryReader for Mutex<T> {
|
||||
fn read_from_slice(
|
||||
&self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
let mut this = self.lock();
|
||||
|
||||
this.read_from_slice_mut(reader, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an inner lock.
|
||||
impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Box<T, A> {
|
||||
fn read_from_slice(
|
||||
&self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().read_from_slice(reader, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with an inner lock.
|
||||
impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Pin<Box<T, A>> {
|
||||
fn read_from_slice(
|
||||
&self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().read_from_slice(reader, offset)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate for `Arc<T>`: Support an `Arc<T>` with an inner lock.
|
||||
impl<T: ?Sized + BinaryReader> BinaryReader for Arc<T> {
|
||||
fn read_from_slice(
|
||||
&self,
|
||||
reader: &mut UserSliceReader,
|
||||
offset: &mut file::Offset,
|
||||
) -> Result<usize> {
|
||||
self.deref().read_from_slice(reader, offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
use crate::{
|
||||
bindings, fmt,
|
||||
prelude::*,
|
||||
sync::aref::ARef,
|
||||
types::{ForeignOwnable, Opaque},
|
||||
};
|
||||
use core::{marker::PhantomData, ptr};
|
||||
use core::{any::TypeId, marker::PhantomData, ptr};
|
||||
|
||||
#[cfg(CONFIG_PRINTK)]
|
||||
use crate::c_str;
|
||||
@@ -17,6 +18,9 @@ use crate::str::CStrExt as _;
|
||||
|
||||
pub mod property;
|
||||
|
||||
// Assert that we can `read()` / `write()` a `TypeId` instance from / into `struct driver_type`.
|
||||
static_assert!(core::mem::size_of::<bindings::driver_type>() >= core::mem::size_of::<TypeId>());
|
||||
|
||||
/// The core representation of a device in the kernel's driver model.
|
||||
///
|
||||
/// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either
|
||||
@@ -198,10 +202,31 @@ impl Device {
|
||||
}
|
||||
|
||||
impl Device<CoreInternal> {
|
||||
/// Store a pointer to the bound driver's private data.
|
||||
pub fn set_drvdata(&self, data: impl ForeignOwnable) {
|
||||
fn set_type_id<T: 'static>(&self) {
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) }
|
||||
let private = unsafe { (*self.as_raw()).p };
|
||||
|
||||
// SAFETY: For a bound device (implied by the `CoreInternal` device context), `private` is
|
||||
// guaranteed to be a valid pointer to a `struct device_private`.
|
||||
let driver_type = unsafe { &raw mut (*private).driver_type };
|
||||
|
||||
// SAFETY: `driver_type` is valid for (unaligned) writes of a `TypeId`.
|
||||
unsafe {
|
||||
driver_type
|
||||
.cast::<TypeId>()
|
||||
.write_unaligned(TypeId::of::<T>())
|
||||
};
|
||||
}
|
||||
|
||||
/// Store a pointer to the bound driver's private data.
|
||||
pub fn set_drvdata<T: 'static>(&self, data: impl PinInit<T, Error>) -> Result {
|
||||
let data = KBox::pin_init(data, GFP_KERNEL)?;
|
||||
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) };
|
||||
self.set_type_id::<T>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Take ownership of the private data stored in this [`Device`].
|
||||
@@ -211,16 +236,19 @@ impl Device<CoreInternal> {
|
||||
/// - Must only be called once after a preceding call to [`Device::set_drvdata`].
|
||||
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
|
||||
/// [`Device::set_drvdata`].
|
||||
pub unsafe fn drvdata_obtain<T: ForeignOwnable>(&self) -> T {
|
||||
pub unsafe fn drvdata_obtain<T: 'static>(&self) -> Pin<KBox<T>> {
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
|
||||
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
unsafe { bindings::dev_set_drvdata(self.as_raw(), core::ptr::null_mut()) };
|
||||
|
||||
// SAFETY:
|
||||
// - By the safety requirements of this function, `ptr` comes from a previous call to
|
||||
// `into_foreign()`.
|
||||
// - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
|
||||
// in `into_foreign()`.
|
||||
unsafe { T::from_foreign(ptr.cast()) }
|
||||
unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) }
|
||||
}
|
||||
|
||||
/// Borrow the driver's private data bound to this [`Device`].
|
||||
@@ -231,7 +259,23 @@ impl Device<CoreInternal> {
|
||||
/// [`Device::drvdata_obtain`].
|
||||
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
|
||||
/// [`Device::set_drvdata`].
|
||||
pub unsafe fn drvdata_borrow<T: ForeignOwnable>(&self) -> T::Borrowed<'_> {
|
||||
pub unsafe fn drvdata_borrow<T: 'static>(&self) -> Pin<&T> {
|
||||
// SAFETY: `drvdata_unchecked()` has the exact same safety requirements as the ones
|
||||
// required by this method.
|
||||
unsafe { self.drvdata_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<Bound> {
|
||||
/// Borrow the driver's private data bound to this [`Device`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Must only be called after a preceding call to [`Device::set_drvdata`] and before
|
||||
/// [`Device::drvdata_obtain`].
|
||||
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
|
||||
/// [`Device::set_drvdata`].
|
||||
unsafe fn drvdata_unchecked<T: 'static>(&self) -> Pin<&T> {
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
|
||||
|
||||
@@ -240,7 +284,46 @@ impl Device<CoreInternal> {
|
||||
// `into_foreign()`.
|
||||
// - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
|
||||
// in `into_foreign()`.
|
||||
unsafe { T::borrow(ptr.cast()) }
|
||||
unsafe { Pin::<KBox<T>>::borrow(ptr.cast()) }
|
||||
}
|
||||
|
||||
fn match_type_id<T: 'static>(&self) -> Result {
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
let private = unsafe { (*self.as_raw()).p };
|
||||
|
||||
// SAFETY: For a bound device, `private` is guaranteed to be a valid pointer to a
|
||||
// `struct device_private`.
|
||||
let driver_type = unsafe { &raw mut (*private).driver_type };
|
||||
|
||||
// SAFETY:
|
||||
// - `driver_type` is valid for (unaligned) reads of a `TypeId`.
|
||||
// - A bound device guarantees that `driver_type` contains a valid `TypeId` value.
|
||||
let type_id = unsafe { driver_type.cast::<TypeId>().read_unaligned() };
|
||||
|
||||
if type_id != TypeId::of::<T>() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Access a driver's private data.
|
||||
///
|
||||
/// Returns a pinned reference to the driver's private data or [`EINVAL`] if it doesn't match
|
||||
/// the asserted type `T`.
|
||||
pub fn drvdata<T: 'static>(&self) -> Result<Pin<&T>> {
|
||||
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
|
||||
if unsafe { bindings::dev_get_drvdata(self.as_raw()) }.is_null() {
|
||||
return Err(ENOENT);
|
||||
}
|
||||
|
||||
self.match_type_id::<T>()?;
|
||||
|
||||
// SAFETY:
|
||||
// - The above check of `dev_get_drvdata()` guarantees that we are called after
|
||||
// `set_drvdata()` and before `drvdata_obtain()`.
|
||||
// - We've just checked that the type of the driver's private data is in fact `T`.
|
||||
Ok(unsafe { self.drvdata_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +595,39 @@ impl DeviceContext for Core {}
|
||||
impl DeviceContext for CoreInternal {}
|
||||
impl DeviceContext for Normal {}
|
||||
|
||||
/// Convert device references to bus device references.
|
||||
///
|
||||
/// Bus devices can implement this trait to allow abstractions to provide the bus device in
|
||||
/// class device callbacks.
|
||||
///
|
||||
/// This must not be used by drivers and is intended for bus and class device abstractions only.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `AsBusDevice::OFFSET` must be the offset of the embedded base `struct device` field within a
|
||||
/// bus device structure.
|
||||
pub unsafe trait AsBusDevice<Ctx: DeviceContext>: AsRef<Device<Ctx>> {
|
||||
/// The relative offset to the device field.
|
||||
///
|
||||
/// Use `offset_of!(bindings, field)` macro to avoid breakage.
|
||||
const OFFSET: usize;
|
||||
|
||||
/// Convert a reference to [`Device`] into `Self`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `dev` must be contained in `Self`.
|
||||
unsafe fn from_device(dev: &Device<Ctx>) -> &Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let raw = dev.as_raw();
|
||||
// SAFETY: `raw - Self::OFFSET` is guaranteed by the safety requirements
|
||||
// to be a valid pointer to `Self`.
|
||||
unsafe { &*raw.byte_sub(Self::OFFSET).cast::<Self>() }
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
|
||||
|
||||
@@ -52,8 +52,20 @@ struct Inner<T: Send> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use kernel::{bindings, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}};
|
||||
/// # use core::ops::Deref;
|
||||
/// use kernel::{
|
||||
/// bindings,
|
||||
/// device::{
|
||||
/// Bound,
|
||||
/// Device,
|
||||
/// },
|
||||
/// devres::Devres,
|
||||
/// io::{
|
||||
/// Io,
|
||||
/// IoRaw,
|
||||
/// PhysAddr,
|
||||
/// },
|
||||
/// };
|
||||
/// use core::ops::Deref;
|
||||
///
|
||||
/// // See also [`pci::Bar`] for a real example.
|
||||
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
|
||||
@@ -66,7 +78,7 @@ struct Inner<T: Send> {
|
||||
/// unsafe fn new(paddr: usize) -> Result<Self>{
|
||||
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
|
||||
/// // valid for `ioremap`.
|
||||
/// let addr = unsafe { bindings::ioremap(paddr as bindings::phys_addr_t, SIZE) };
|
||||
/// let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) };
|
||||
/// if addr.is_null() {
|
||||
/// return Err(ENOMEM);
|
||||
/// }
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{
|
||||
sync::aref::ARef,
|
||||
transmute::{AsBytes, FromBytes},
|
||||
};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
/// DMA address type.
|
||||
///
|
||||
@@ -358,7 +359,7 @@ pub struct CoherentAllocation<T: AsBytes + FromBytes> {
|
||||
dev: ARef<device::Device>,
|
||||
dma_handle: DmaAddress,
|
||||
count: usize,
|
||||
cpu_addr: *mut T,
|
||||
cpu_addr: NonNull<T>,
|
||||
dma_attrs: Attrs,
|
||||
}
|
||||
|
||||
@@ -392,7 +393,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
.ok_or(EOVERFLOW)?;
|
||||
let mut dma_handle = 0;
|
||||
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
|
||||
let ret = unsafe {
|
||||
let addr = unsafe {
|
||||
bindings::dma_alloc_attrs(
|
||||
dev.as_raw(),
|
||||
size,
|
||||
@@ -401,9 +402,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
dma_attrs.as_raw(),
|
||||
)
|
||||
};
|
||||
if ret.is_null() {
|
||||
return Err(ENOMEM);
|
||||
}
|
||||
let addr = NonNull::new(addr).ok_or(ENOMEM)?;
|
||||
// INVARIANT:
|
||||
// - We just successfully allocated a coherent region which is accessible for
|
||||
// `count` elements, hence the cpu address is valid. We also hold a refcounted reference
|
||||
@@ -414,7 +413,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
dev: dev.into(),
|
||||
dma_handle,
|
||||
count,
|
||||
cpu_addr: ret.cast::<T>(),
|
||||
cpu_addr: addr.cast(),
|
||||
dma_attrs,
|
||||
})
|
||||
}
|
||||
@@ -446,13 +445,13 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
|
||||
/// Returns the base address to the allocated region in the CPU's virtual address space.
|
||||
pub fn start_ptr(&self) -> *const T {
|
||||
self.cpu_addr
|
||||
self.cpu_addr.as_ptr()
|
||||
}
|
||||
|
||||
/// Returns the base address to the allocated region in the CPU's virtual address space as
|
||||
/// a mutable pointer.
|
||||
pub fn start_ptr_mut(&mut self) -> *mut T {
|
||||
self.cpu_addr
|
||||
self.cpu_addr.as_ptr()
|
||||
}
|
||||
|
||||
/// Returns a DMA handle which may be given to the device as the DMA address base of
|
||||
@@ -505,7 +504,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
// data is also guaranteed by the safety requirements of the function.
|
||||
// - `offset + count` can't overflow since it is smaller than `self.count` and we've checked
|
||||
// that `self.count` won't overflow early in the constructor.
|
||||
Ok(unsafe { core::slice::from_raw_parts(self.cpu_addr.add(offset), count) })
|
||||
Ok(unsafe { core::slice::from_raw_parts(self.start_ptr().add(offset), count) })
|
||||
}
|
||||
|
||||
/// Performs the same functionality as [`CoherentAllocation::as_slice`], except that a mutable
|
||||
@@ -525,7 +524,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
// data is also guaranteed by the safety requirements of the function.
|
||||
// - `offset + count` can't overflow since it is smaller than `self.count` and we've checked
|
||||
// that `self.count` won't overflow early in the constructor.
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(self.cpu_addr.add(offset), count) })
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(self.start_ptr_mut().add(offset), count) })
|
||||
}
|
||||
|
||||
/// Writes data to the region starting from `offset`. `offset` is in units of `T`, not the
|
||||
@@ -557,7 +556,11 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
// - `offset + count` can't overflow since it is smaller than `self.count` and we've checked
|
||||
// that `self.count` won't overflow early in the constructor.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(src.as_ptr(), self.cpu_addr.add(offset), src.len())
|
||||
core::ptr::copy_nonoverlapping(
|
||||
src.as_ptr(),
|
||||
self.start_ptr_mut().add(offset),
|
||||
src.len(),
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@@ -576,7 +579,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
||||
// and we've just checked that the range and index is within bounds.
|
||||
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
|
||||
// that `self.count` won't overflow early in the constructor.
|
||||
Ok(unsafe { self.cpu_addr.add(offset) })
|
||||
Ok(unsafe { self.cpu_addr.as_ptr().add(offset) })
|
||||
}
|
||||
|
||||
/// Reads the value of `field` and ensures that its type is [`FromBytes`].
|
||||
@@ -637,7 +640,7 @@ impl<T: AsBytes + FromBytes> Drop for CoherentAllocation<T> {
|
||||
bindings::dma_free_attrs(
|
||||
self.dev.as_raw(),
|
||||
size,
|
||||
self.cpu_addr.cast(),
|
||||
self.start_ptr_mut().cast(),
|
||||
self.dma_handle,
|
||||
self.dma_attrs.as_raw(),
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
//! const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
|
||||
//!
|
||||
//! /// Driver probe.
|
||||
//! fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
|
||||
//! fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
|
||||
//!
|
||||
//! /// Driver unbind (optional).
|
||||
//! fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
|
||||
@@ -35,7 +35,7 @@
|
||||
//!
|
||||
//! For specific examples see [`auxiliary::Driver`], [`pci::Driver`] and [`platform::Driver`].
|
||||
//!
|
||||
//! The `probe()` callback should return a `Result<Pin<KBox<Self>>>`, i.e. the driver's private
|
||||
//! The `probe()` callback should return a `impl PinInit<Self, Error>`, i.e. the driver's private
|
||||
//! data. The bus abstraction should store the pointer in the corresponding bus device. The generic
|
||||
//! [`Device`] infrastructure provides common helpers for this purpose on its
|
||||
//! [`Device<CoreInternal>`] implementation.
|
||||
|
||||
@@ -17,6 +17,11 @@ use crate::{
|
||||
};
|
||||
use core::ptr;
|
||||
|
||||
/// Primitive type representing the offset within a [`File`].
|
||||
///
|
||||
/// Type alias for `bindings::loff_t`.
|
||||
pub type Offset = bindings::loff_t;
|
||||
|
||||
/// Flags associated with a [`File`].
|
||||
pub mod flags {
|
||||
/// File is opened in append mode.
|
||||
|
||||
586
rust/kernel/i2c.rs
Normal file
586
rust/kernel/i2c.rs
Normal file
@@ -0,0 +1,586 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! I2C Driver subsystem
|
||||
|
||||
// I2C Driver abstractions.
|
||||
use crate::{
|
||||
acpi,
|
||||
container_of,
|
||||
device,
|
||||
device_id::{
|
||||
RawDeviceId,
|
||||
RawDeviceIdIndex, //
|
||||
},
|
||||
devres::Devres,
|
||||
driver,
|
||||
error::*,
|
||||
of,
|
||||
prelude::*,
|
||||
types::{
|
||||
AlwaysRefCounted,
|
||||
Opaque, //
|
||||
}, //
|
||||
};
|
||||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::offset_of,
|
||||
ptr::{
|
||||
from_ref,
|
||||
NonNull, //
|
||||
}, //
|
||||
};
|
||||
|
||||
use kernel::types::ARef;
|
||||
|
||||
/// An I2C device id table.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DeviceId(bindings::i2c_device_id);
|
||||
|
||||
impl DeviceId {
|
||||
const I2C_NAME_SIZE: usize = 20;
|
||||
|
||||
/// Create a new device id from an I2C 'id' string.
|
||||
#[inline(always)]
|
||||
pub const fn new(id: &'static CStr) -> Self {
|
||||
let src = id.to_bytes_with_nul();
|
||||
build_assert!(src.len() <= Self::I2C_NAME_SIZE, "ID exceeds 20 bytes");
|
||||
let mut i2c: bindings::i2c_device_id = pin_init::zeroed();
|
||||
let mut i = 0;
|
||||
while i < src.len() {
|
||||
i2c.name[i] = src[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Self(i2c)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `i2c_device_id` and does not add
|
||||
// additional invariants, so it's safe to transmute to `RawType`.
|
||||
unsafe impl RawDeviceId for DeviceId {
|
||||
type RawType = bindings::i2c_device_id;
|
||||
}
|
||||
|
||||
// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
|
||||
unsafe impl RawDeviceIdIndex for DeviceId {
|
||||
const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data);
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.0.driver_data
|
||||
}
|
||||
}
|
||||
|
||||
/// IdTable type for I2C
|
||||
pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
|
||||
|
||||
/// Create a I2C `IdTable` with its alias for modpost.
|
||||
#[macro_export]
|
||||
macro_rules! i2c_device_table {
|
||||
($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
|
||||
const $table_name: $crate::device_id::IdArray<
|
||||
$crate::i2c::DeviceId,
|
||||
$id_info_type,
|
||||
{ $table_data.len() },
|
||||
> = $crate::device_id::IdArray::new($table_data);
|
||||
|
||||
$crate::module_device_table!("i2c", $module_table_name, $table_name);
|
||||
};
|
||||
}
|
||||
|
||||
/// An adapter for the registration of I2C drivers.
|
||||
pub struct Adapter<T: Driver>(T);
|
||||
|
||||
// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
|
||||
// a preceding call to `register` has been successful.
|
||||
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
|
||||
type RegType = bindings::i2c_driver;
|
||||
|
||||
unsafe fn register(
|
||||
idrv: &Opaque<Self::RegType>,
|
||||
name: &'static CStr,
|
||||
module: &'static ThisModule,
|
||||
) -> Result {
|
||||
build_assert!(
|
||||
T::ACPI_ID_TABLE.is_some() || T::OF_ID_TABLE.is_some() || T::I2C_ID_TABLE.is_some(),
|
||||
"At least one of ACPI/OF/Legacy tables must be present when registering an i2c driver"
|
||||
);
|
||||
|
||||
let i2c_table = match T::I2C_ID_TABLE {
|
||||
Some(table) => table.as_ptr(),
|
||||
None => core::ptr::null(),
|
||||
};
|
||||
|
||||
let of_table = match T::OF_ID_TABLE {
|
||||
Some(table) => table.as_ptr(),
|
||||
None => core::ptr::null(),
|
||||
};
|
||||
|
||||
let acpi_table = match T::ACPI_ID_TABLE {
|
||||
Some(table) => table.as_ptr(),
|
||||
None => core::ptr::null(),
|
||||
};
|
||||
|
||||
// SAFETY: It's safe to set the fields of `struct i2c_client` on initialization.
|
||||
unsafe {
|
||||
(*idrv.get()).driver.name = name.as_char_ptr();
|
||||
(*idrv.get()).probe = Some(Self::probe_callback);
|
||||
(*idrv.get()).remove = Some(Self::remove_callback);
|
||||
(*idrv.get()).shutdown = Some(Self::shutdown_callback);
|
||||
(*idrv.get()).id_table = i2c_table;
|
||||
(*idrv.get()).driver.of_match_table = of_table;
|
||||
(*idrv.get()).driver.acpi_match_table = acpi_table;
|
||||
}
|
||||
|
||||
// SAFETY: `idrv` is guaranteed to be a valid `RegType`.
|
||||
to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
|
||||
}
|
||||
|
||||
unsafe fn unregister(idrv: &Opaque<Self::RegType>) {
|
||||
// SAFETY: `idrv` is guaranteed to be a valid `RegType`.
|
||||
unsafe { bindings::i2c_del_driver(idrv.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Driver + 'static> Adapter<T> {
|
||||
extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_int {
|
||||
// SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a
|
||||
// `struct i2c_client`.
|
||||
//
|
||||
// INVARIANT: `idev` is valid for the duration of `probe_callback()`.
|
||||
let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
|
||||
|
||||
let info =
|
||||
Self::i2c_id_info(idev).or_else(|| <Self as driver::Adapter>::id_info(idev.as_ref()));
|
||||
|
||||
from_result(|| {
|
||||
let data = T::probe(idev, info);
|
||||
|
||||
idev.as_ref().set_drvdata(data)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn remove_callback(idev: *mut bindings::i2c_client) {
|
||||
// SAFETY: `idev` is a valid pointer to a `struct i2c_client`.
|
||||
let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
|
||||
|
||||
// SAFETY: `remove_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
|
||||
|
||||
T::unbind(idev, data.as_ref());
|
||||
}
|
||||
|
||||
extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
|
||||
// SAFETY: `shutdown_callback` is only ever called for a valid `idev`
|
||||
let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
|
||||
|
||||
// SAFETY: `shutdown_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
|
||||
|
||||
T::shutdown(idev, data.as_ref());
|
||||
}
|
||||
|
||||
/// The [`i2c::IdTable`] of the corresponding driver.
|
||||
fn i2c_id_table() -> Option<IdTable<<Self as driver::Adapter>::IdInfo>> {
|
||||
T::I2C_ID_TABLE
|
||||
}
|
||||
|
||||
/// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any.
|
||||
///
|
||||
/// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`].
|
||||
fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter>::IdInfo> {
|
||||
let table = Self::i2c_id_table()?;
|
||||
|
||||
// SAFETY:
|
||||
// - `table` has static lifetime, hence it's valid for reads
|
||||
// - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
|
||||
let raw_id = unsafe { bindings::i2c_match_id(table.as_ptr(), dev.as_raw()) };
|
||||
|
||||
if raw_id.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct i2c_device_id` and
|
||||
// does not add additional invariants, so it's safe to transmute.
|
||||
let id = unsafe { &*raw_id.cast::<DeviceId>() };
|
||||
|
||||
Some(table.info(<DeviceId as RawDeviceIdIndex>::index(id)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
|
||||
type IdInfo = T::IdInfo;
|
||||
|
||||
fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
|
||||
T::OF_ID_TABLE
|
||||
}
|
||||
|
||||
fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
|
||||
T::ACPI_ID_TABLE
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares a kernel module that exposes a single i2c driver.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// kernel::module_i2c_driver! {
|
||||
/// type: MyDriver,
|
||||
/// name: "Module name",
|
||||
/// authors: ["Author name"],
|
||||
/// description: "Description",
|
||||
/// license: "GPL v2",
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! module_i2c_driver {
|
||||
($($f:tt)*) => {
|
||||
$crate::module_driver!(<T>, $crate::i2c::Adapter<T>, { $($f)* });
|
||||
};
|
||||
}
|
||||
|
||||
/// The i2c driver trait.
|
||||
///
|
||||
/// Drivers must implement this trait in order to get a i2c driver registered.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
///```
|
||||
/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of};
|
||||
///
|
||||
/// struct MyDriver;
|
||||
///
|
||||
/// kernel::acpi_device_table!(
|
||||
/// ACPI_TABLE,
|
||||
/// MODULE_ACPI_TABLE,
|
||||
/// <MyDriver as i2c::Driver>::IdInfo,
|
||||
/// [
|
||||
/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ())
|
||||
/// ]
|
||||
/// );
|
||||
///
|
||||
/// kernel::i2c_device_table!(
|
||||
/// I2C_TABLE,
|
||||
/// MODULE_I2C_TABLE,
|
||||
/// <MyDriver as i2c::Driver>::IdInfo,
|
||||
/// [
|
||||
/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ())
|
||||
/// ]
|
||||
/// );
|
||||
///
|
||||
/// kernel::of_device_table!(
|
||||
/// OF_TABLE,
|
||||
/// MODULE_OF_TABLE,
|
||||
/// <MyDriver as i2c::Driver>::IdInfo,
|
||||
/// [
|
||||
/// (of::DeviceId::new(c_str!("test,device")), ())
|
||||
/// ]
|
||||
/// );
|
||||
///
|
||||
/// impl i2c::Driver for MyDriver {
|
||||
/// type IdInfo = ();
|
||||
/// const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
|
||||
/// 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(
|
||||
/// _idev: &i2c::I2cClient<Core>,
|
||||
/// _id_info: Option<&Self::IdInfo>,
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// Err(ENODEV)
|
||||
/// }
|
||||
///
|
||||
/// fn shutdown(_idev: &i2c::I2cClient<Core>, this: Pin<&Self>) {
|
||||
/// }
|
||||
/// }
|
||||
///```
|
||||
pub trait Driver: Send {
|
||||
/// The type holding information about each device id supported by the driver.
|
||||
// TODO: Use `associated_type_defaults` once stabilized:
|
||||
//
|
||||
// ```
|
||||
// type IdInfo: 'static = ();
|
||||
// ```
|
||||
type IdInfo: 'static;
|
||||
|
||||
/// The table of device ids supported by the driver.
|
||||
const I2C_ID_TABLE: Option<IdTable<Self::IdInfo>> = None;
|
||||
|
||||
/// The table of OF device ids supported by the driver.
|
||||
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
|
||||
|
||||
/// The table of ACPI device ids supported by the driver.
|
||||
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
|
||||
|
||||
/// I2C driver probe.
|
||||
///
|
||||
/// Called when a new i2c client is added or discovered.
|
||||
/// Implementers should attempt to initialize the client here.
|
||||
fn probe(
|
||||
dev: &I2cClient<device::Core>,
|
||||
id_info: Option<&Self::IdInfo>,
|
||||
) -> impl PinInit<Self, Error>;
|
||||
|
||||
/// I2C driver shutdown.
|
||||
///
|
||||
/// Called by the kernel during system reboot or power-off to allow the [`Driver`] to bring the
|
||||
/// [`I2cClient`] into a safe state. Implementing this callback is optional.
|
||||
///
|
||||
/// Typical actions include stopping transfers, disabling interrupts, or resetting the hardware
|
||||
/// to prevent undesired behavior during shutdown.
|
||||
///
|
||||
/// This callback is distinct from final resource cleanup, as the driver instance remains valid
|
||||
/// after it returns. Any deallocation or teardown of driver-owned resources should instead be
|
||||
/// handled in `Self::drop`.
|
||||
fn shutdown(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
|
||||
let _ = (dev, this);
|
||||
}
|
||||
|
||||
/// I2C driver unbind.
|
||||
///
|
||||
/// Called when the [`I2cClient`] is unbound from its bound [`Driver`]. Implementing this
|
||||
/// callback is optional.
|
||||
///
|
||||
/// This callback serves as a place for drivers to perform teardown operations that require a
|
||||
/// `&Device<Core>` or `&Device<Bound>` reference. For instance, drivers may try to perform I/O
|
||||
/// operations to gracefully tear down the device.
|
||||
///
|
||||
/// Otherwise, release operations for driver resources should be performed in `Self::drop`.
|
||||
fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
|
||||
let _ = (dev, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// The i2c adapter representation.
|
||||
///
|
||||
/// This structure represents the Rust abstraction for a C `struct i2c_adapter`. The
|
||||
/// implementation abstracts the usage of an existing C `struct i2c_adapter` that
|
||||
/// gets passed from the C side
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` created by the C portion of
|
||||
/// the kernel.
|
||||
#[repr(transparent)]
|
||||
pub struct I2cAdapter<Ctx: device::DeviceContext = device::Normal>(
|
||||
Opaque<bindings::i2c_adapter>,
|
||||
PhantomData<Ctx>,
|
||||
);
|
||||
|
||||
impl<Ctx: device::DeviceContext> I2cAdapter<Ctx> {
|
||||
fn as_raw(&self) -> *mut bindings::i2c_adapter {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2cAdapter {
|
||||
/// Returns the I2C Adapter index.
|
||||
#[inline]
|
||||
pub fn index(&self) -> i32 {
|
||||
// SAFETY: `self.as_raw` is a valid pointer to a `struct i2c_adapter`.
|
||||
unsafe { (*self.as_raw()).nr }
|
||||
}
|
||||
|
||||
/// Gets pointer to an `i2c_adapter` by index.
|
||||
pub fn get(index: i32) -> Result<ARef<Self>> {
|
||||
// SAFETY: `index` must refer to a valid I2C adapter; the kernel
|
||||
// guarantees that `i2c_get_adapter(index)` returns either a valid
|
||||
// pointer or NULL. `NonNull::new` guarantees the correct check.
|
||||
let adapter = NonNull::new(unsafe { bindings::i2c_get_adapter(index) }).ok_or(ENODEV)?;
|
||||
|
||||
// SAFETY: `adapter` is non-null and points to a live `i2c_adapter`.
|
||||
// `I2cAdapter` is #[repr(transparent)], so this cast is valid.
|
||||
Ok(unsafe { (&*adapter.as_ptr().cast::<I2cAdapter<device::Normal>>()).into() })
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't depend on
|
||||
// `I2cAdapter`'s generic argument.
|
||||
kernel::impl_device_context_deref!(unsafe { I2cAdapter });
|
||||
kernel::impl_device_context_into_aref!(I2cAdapter);
|
||||
|
||||
// SAFETY: Instances of `I2cAdapter` are always reference-counted.
|
||||
unsafe impl crate::types::AlwaysRefCounted for I2cAdapter {
|
||||
fn inc_ref(&self) {
|
||||
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||
unsafe { bindings::i2c_get_adapter(self.index()) };
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||||
unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// The i2c board info representation
|
||||
///
|
||||
/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure,
|
||||
/// which is used for manual I2C client creation.
|
||||
#[repr(transparent)]
|
||||
pub struct I2cBoardInfo(bindings::i2c_board_info);
|
||||
|
||||
impl I2cBoardInfo {
|
||||
const I2C_TYPE_SIZE: usize = 20;
|
||||
/// Create a new [`I2cBoardInfo`] for a kernel driver.
|
||||
#[inline(always)]
|
||||
pub const fn new(type_: &'static CStr, addr: u16) -> Self {
|
||||
let src = type_.to_bytes_with_nul();
|
||||
build_assert!(src.len() <= Self::I2C_TYPE_SIZE, "Type exceeds 20 bytes");
|
||||
let mut i2c_board_info: bindings::i2c_board_info = pin_init::zeroed();
|
||||
let mut i: usize = 0;
|
||||
while i < src.len() {
|
||||
i2c_board_info.type_[i] = src[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
i2c_board_info.addr = addr;
|
||||
Self(i2c_board_info)
|
||||
}
|
||||
|
||||
fn as_raw(&self) -> *const bindings::i2c_board_info {
|
||||
from_ref(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The i2c client representation.
|
||||
///
|
||||
/// This structure represents the Rust abstraction for a C `struct i2c_client`. The
|
||||
/// implementation abstracts the usage of an existing C `struct i2c_client` that
|
||||
/// gets passed from the C side
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`I2cClient`] instance represents a valid `struct i2c_client` created by the C portion of
|
||||
/// the kernel.
|
||||
#[repr(transparent)]
|
||||
pub struct I2cClient<Ctx: device::DeviceContext = device::Normal>(
|
||||
Opaque<bindings::i2c_client>,
|
||||
PhantomData<Ctx>,
|
||||
);
|
||||
|
||||
impl<Ctx: device::DeviceContext> I2cClient<Ctx> {
|
||||
fn as_raw(&self) -> *mut bindings::i2c_client {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`.
|
||||
// The offset is guaranteed to point to a valid device field inside `I2cClient`.
|
||||
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for I2cClient<Ctx> {
|
||||
const OFFSET: usize = offset_of!(bindings::i2c_client, dev);
|
||||
}
|
||||
|
||||
// SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't depend on
|
||||
// `I2cClient`'s generic argument.
|
||||
kernel::impl_device_context_deref!(unsafe { I2cClient });
|
||||
kernel::impl_device_context_into_aref!(I2cClient);
|
||||
|
||||
// SAFETY: Instances of `I2cClient` are always reference-counted.
|
||||
unsafe impl AlwaysRefCounted for I2cClient {
|
||||
fn inc_ref(&self) {
|
||||
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||
unsafe { bindings::get_device(self.as_ref().as_raw()) };
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||||
unsafe { bindings::put_device(&raw mut (*obj.as_ref().as_raw()).dev) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for I2cClient<Ctx> {
|
||||
fn as_ref(&self) -> &device::Device<Ctx> {
|
||||
let raw = self.as_raw();
|
||||
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
|
||||
// `struct i2c_client`.
|
||||
let dev = unsafe { &raw mut (*raw).dev };
|
||||
|
||||
// SAFETY: `dev` points to a valid `struct device`.
|
||||
unsafe { device::Device::from_raw(dev) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &I2cClient<Ctx> {
|
||||
type Error = kernel::error::Error;
|
||||
|
||||
fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
|
||||
// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
|
||||
// `struct device`.
|
||||
if unsafe { bindings::i2c_verify_client(dev.as_raw()).is_null() } {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: We've just verified that the type of `dev` equals to
|
||||
// `bindings::i2c_client_type`, hence `dev` must be embedded in a valid
|
||||
// `struct i2c_client` as guaranteed by the corresponding C code.
|
||||
let idev = unsafe { container_of!(dev.as_raw(), bindings::i2c_client, dev) };
|
||||
|
||||
// SAFETY: `idev` is a valid pointer to a `struct i2c_client`.
|
||||
Ok(unsafe { &*idev.cast() })
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `I2cClient` is always reference-counted and can be released from any thread.
|
||||
unsafe impl Send for I2cClient {}
|
||||
|
||||
// SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
|
||||
// (i.e. `I2cClient<Normal>) are thread safe.
|
||||
unsafe impl Sync for I2cClient {}
|
||||
|
||||
/// The registration of an i2c client device.
|
||||
///
|
||||
/// This type represents the registration of a [`struct i2c_client`]. When an instance of this
|
||||
/// type is dropped, its respective i2c client device will be unregistered from the system.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `self.0` always holds a valid pointer to an initialized and registered
|
||||
/// [`struct i2c_client`].
|
||||
#[repr(transparent)]
|
||||
pub struct Registration(NonNull<bindings::i2c_client>);
|
||||
|
||||
impl Registration {
|
||||
/// The C `i2c_new_client_device` function wrapper for manual I2C client creation.
|
||||
pub fn new<'a>(
|
||||
i2c_adapter: &I2cAdapter,
|
||||
i2c_board_info: &I2cBoardInfo,
|
||||
parent_dev: &'a device::Device<device::Bound>,
|
||||
) -> impl PinInit<Devres<Self>, Error> + 'a {
|
||||
Devres::new(parent_dev, Self::try_new(i2c_adapter, i2c_board_info))
|
||||
}
|
||||
|
||||
fn try_new(i2c_adapter: &I2cAdapter, i2c_board_info: &I2cBoardInfo) -> Result<Self> {
|
||||
// SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid
|
||||
// pointer or NULL. `from_err_ptr` separates errors. Following `NonNull::new`
|
||||
// checks for NULL.
|
||||
let raw_dev = from_err_ptr(unsafe {
|
||||
bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_board_info.as_raw())
|
||||
})?;
|
||||
|
||||
let dev_ptr = NonNull::new(raw_dev).ok_or(ENODEV)?;
|
||||
|
||||
Ok(Self(dev_ptr))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Registration {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: `Drop` is only called for a valid `Registration`, which by invariant
|
||||
// always contains a non-null pointer to an `i2c_client`.
|
||||
unsafe { bindings::i2c_unregister_device(self.0.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `Registration` of a `struct i2c_client` can be released from any thread.
|
||||
unsafe impl Send for Registration {}
|
||||
|
||||
// SAFETY: `Registration` offers no interior mutability (no mutation through &self
|
||||
// and no mutable access is exposed)
|
||||
unsafe impl Sync for Registration {}
|
||||
@@ -4,8 +4,10 @@
|
||||
//!
|
||||
//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
|
||||
|
||||
use crate::error::{code::EINVAL, Result};
|
||||
use crate::{bindings, build_assert, ffi::c_void};
|
||||
use crate::{
|
||||
bindings,
|
||||
prelude::*, //
|
||||
};
|
||||
|
||||
pub mod mem;
|
||||
pub mod poll;
|
||||
@@ -13,6 +15,18 @@ pub mod resource;
|
||||
|
||||
pub use resource::Resource;
|
||||
|
||||
/// Physical address type.
|
||||
///
|
||||
/// This is a type alias to either `u32` or `u64` depending on the config option
|
||||
/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
|
||||
pub type PhysAddr = bindings::phys_addr_t;
|
||||
|
||||
/// Resource Size type.
|
||||
///
|
||||
/// This is a type alias to either `u32` or `u64` depending on the config option
|
||||
/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
|
||||
pub type ResourceSize = bindings::resource_size_t;
|
||||
|
||||
/// Raw representation of an MMIO region.
|
||||
///
|
||||
/// By itself, the existence of an instance of this structure does not provide any guarantees that
|
||||
@@ -62,8 +76,16 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use kernel::{bindings, ffi::c_void, io::{Io, IoRaw}};
|
||||
/// # use core::ops::Deref;
|
||||
/// use kernel::{
|
||||
/// bindings,
|
||||
/// ffi::c_void,
|
||||
/// io::{
|
||||
/// Io,
|
||||
/// IoRaw,
|
||||
/// PhysAddr,
|
||||
/// },
|
||||
/// };
|
||||
/// use core::ops::Deref;
|
||||
///
|
||||
/// // See also [`pci::Bar`] for a real example.
|
||||
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
|
||||
@@ -76,7 +98,7 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||
/// unsafe fn new(paddr: usize) -> Result<Self>{
|
||||
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
|
||||
/// // valid for `ioremap`.
|
||||
/// let addr = unsafe { bindings::ioremap(paddr as bindings::phys_addr_t, SIZE) };
|
||||
/// let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) };
|
||||
/// if addr.is_null() {
|
||||
/// return Err(ENOMEM);
|
||||
/// }
|
||||
|
||||
@@ -4,16 +4,24 @@
|
||||
|
||||
use core::ops::Deref;
|
||||
|
||||
use crate::c_str;
|
||||
use crate::device::Bound;
|
||||
use crate::device::Device;
|
||||
use crate::devres::Devres;
|
||||
use crate::io;
|
||||
use crate::io::resource::Region;
|
||||
use crate::io::resource::Resource;
|
||||
use crate::io::Io;
|
||||
use crate::io::IoRaw;
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
c_str,
|
||||
device::{
|
||||
Bound,
|
||||
Device, //
|
||||
},
|
||||
devres::Devres,
|
||||
io::{
|
||||
self,
|
||||
resource::{
|
||||
Region,
|
||||
Resource, //
|
||||
},
|
||||
Io,
|
||||
IoRaw, //
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// An IO request for a specific device and resource.
|
||||
pub struct IoRequest<'a> {
|
||||
@@ -53,7 +61,7 @@ impl<'a> IoRequest<'a> {
|
||||
/// fn probe(
|
||||
/// pdev: &platform::Device<Core>,
|
||||
/// info: Option<&Self::IdInfo>,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// let offset = 0; // Some offset.
|
||||
///
|
||||
/// // If the size is known at compile time, use [`Self::iomap_sized`].
|
||||
@@ -70,7 +78,7 @@ impl<'a> IoRequest<'a> {
|
||||
///
|
||||
/// io.write32_relaxed(data, offset);
|
||||
///
|
||||
/// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
|
||||
/// # Ok(SampleDriver)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
@@ -111,7 +119,7 @@ impl<'a> IoRequest<'a> {
|
||||
/// fn probe(
|
||||
/// pdev: &platform::Device<Core>,
|
||||
/// info: Option<&Self::IdInfo>,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// let offset = 0; // Some offset.
|
||||
///
|
||||
/// // Unlike [`Self::iomap_sized`], here the size of the memory region
|
||||
@@ -128,7 +136,7 @@ impl<'a> IoRequest<'a> {
|
||||
///
|
||||
/// io.try_write32_relaxed(data, offset)?;
|
||||
///
|
||||
/// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
|
||||
/// # Ok(SampleDriver)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@@ -5,10 +5,18 @@
|
||||
//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h).
|
||||
|
||||
use crate::{
|
||||
error::{code::*, Result},
|
||||
prelude::*,
|
||||
processor::cpu_relax,
|
||||
task::might_sleep,
|
||||
time::{delay::fsleep, Delta, Instant, Monotonic},
|
||||
time::{
|
||||
delay::{
|
||||
fsleep,
|
||||
udelay, //
|
||||
},
|
||||
Delta,
|
||||
Instant,
|
||||
Monotonic, //
|
||||
},
|
||||
};
|
||||
|
||||
/// Polls periodically until a condition is met, an error occurs,
|
||||
@@ -42,8 +50,8 @@ use crate::{
|
||||
///
|
||||
/// const HW_READY: u16 = 0x01;
|
||||
///
|
||||
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
|
||||
/// match read_poll_timeout(
|
||||
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
|
||||
/// read_poll_timeout(
|
||||
/// // The `op` closure reads the value of a specific status register.
|
||||
/// || io.try_read16(0x1000),
|
||||
/// // The `cond` closure takes a reference to the value returned by `op`
|
||||
@@ -51,14 +59,8 @@ use crate::{
|
||||
/// |val: &u16| *val == HW_READY,
|
||||
/// Delta::from_millis(50),
|
||||
/// Delta::from_secs(3),
|
||||
/// ) {
|
||||
/// Ok(_) => {
|
||||
/// // The hardware is ready. The returned value of the `op` closure
|
||||
/// // isn't used.
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// Err(e) => Err(e),
|
||||
/// }
|
||||
/// )?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
@@ -102,3 +104,70 @@ where
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls periodically until a condition is met, an error occurs,
|
||||
/// or the attempt limit is reached.
|
||||
///
|
||||
/// The function repeatedly executes the given operation `op` closure and
|
||||
/// checks its result using the condition closure `cond`.
|
||||
///
|
||||
/// If `cond` returns `true`, the function returns successfully with the result of `op`.
|
||||
/// Otherwise, it performs a busy wait for a duration specified by `delay_delta`
|
||||
/// before executing `op` again.
|
||||
///
|
||||
/// This process continues until either `op` returns an error, `cond`
|
||||
/// returns `true`, or the attempt limit specified by `retry` is reached.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If `op` returns an error, then that error is returned directly.
|
||||
///
|
||||
/// If the attempt limit specified by `retry` is reached, then
|
||||
/// `Err(ETIMEDOUT)` is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use kernel::io::{poll::read_poll_timeout_atomic, Io};
|
||||
/// use kernel::time::Delta;
|
||||
///
|
||||
/// const HW_READY: u16 = 0x01;
|
||||
///
|
||||
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
|
||||
/// read_poll_timeout_atomic(
|
||||
/// // The `op` closure reads the value of a specific status register.
|
||||
/// || io.try_read16(0x1000),
|
||||
/// // The `cond` closure takes a reference to the value returned by `op`
|
||||
/// // and checks whether the hardware is ready.
|
||||
/// |val: &u16| *val == HW_READY,
|
||||
/// Delta::from_micros(50),
|
||||
/// 1000,
|
||||
/// )?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn read_poll_timeout_atomic<Op, Cond, T>(
|
||||
mut op: Op,
|
||||
mut cond: Cond,
|
||||
delay_delta: Delta,
|
||||
retry: usize,
|
||||
) -> Result<T>
|
||||
where
|
||||
Op: FnMut() -> Result<T>,
|
||||
Cond: FnMut(&T) -> bool,
|
||||
{
|
||||
for _ in 0..retry {
|
||||
let val = op()?;
|
||||
if cond(&val) {
|
||||
return Ok(val);
|
||||
}
|
||||
|
||||
if !delay_delta.is_zero() {
|
||||
udelay(delay_delta);
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
Err(ETIMEDOUT)
|
||||
}
|
||||
|
||||
@@ -5,18 +5,21 @@
|
||||
//!
|
||||
//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h)
|
||||
|
||||
use core::ops::Deref;
|
||||
use core::ptr::NonNull;
|
||||
use core::{
|
||||
ops::Deref,
|
||||
ptr::NonNull, //
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::str::{CStr, CString};
|
||||
use crate::types::Opaque;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
str::CString,
|
||||
types::Opaque, //
|
||||
};
|
||||
|
||||
/// Resource Size type.
|
||||
///
|
||||
/// This is a type alias to either `u32` or `u64` depending on the config option
|
||||
/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
|
||||
pub type ResourceSize = bindings::phys_addr_t;
|
||||
pub use super::{
|
||||
PhysAddr,
|
||||
ResourceSize, //
|
||||
};
|
||||
|
||||
/// A region allocated from a parent [`Resource`].
|
||||
///
|
||||
@@ -97,7 +100,7 @@ impl Resource {
|
||||
/// the region, or a part of it, is already in use.
|
||||
pub fn request_region(
|
||||
&self,
|
||||
start: ResourceSize,
|
||||
start: PhysAddr,
|
||||
size: ResourceSize,
|
||||
name: CString,
|
||||
flags: Flags,
|
||||
@@ -131,7 +134,7 @@ impl Resource {
|
||||
}
|
||||
|
||||
/// Returns the start address of the resource.
|
||||
pub fn start(&self) -> ResourceSize {
|
||||
pub fn start(&self) -> PhysAddr {
|
||||
let inner = self.0.get();
|
||||
// SAFETY: Safe as per the invariants of `Resource`.
|
||||
unsafe { (*inner).start }
|
||||
|
||||
@@ -97,6 +97,8 @@ pub mod faux;
|
||||
pub mod firmware;
|
||||
pub mod fmt;
|
||||
pub mod fs;
|
||||
#[cfg(CONFIG_I2C = "y")]
|
||||
pub mod i2c;
|
||||
pub mod id_pool;
|
||||
pub mod init;
|
||||
pub mod io;
|
||||
|
||||
@@ -5,28 +5,47 @@
|
||||
//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
|
||||
|
||||
use crate::{
|
||||
bindings, container_of, device,
|
||||
device_id::{RawDeviceId, RawDeviceIdIndex},
|
||||
devres::Devres,
|
||||
bindings,
|
||||
container_of,
|
||||
device,
|
||||
device_id::{
|
||||
RawDeviceId,
|
||||
RawDeviceIdIndex, //
|
||||
},
|
||||
driver,
|
||||
error::{from_result, to_result, Result},
|
||||
io::{Io, IoRaw},
|
||||
irq::{self, IrqRequest},
|
||||
error::{
|
||||
from_result,
|
||||
to_result, //
|
||||
},
|
||||
prelude::*,
|
||||
str::CStr,
|
||||
sync::aref::ARef,
|
||||
types::Opaque,
|
||||
ThisModule,
|
||||
ThisModule, //
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
mem::offset_of,
|
||||
ptr::{
|
||||
addr_of_mut,
|
||||
NonNull, //
|
||||
},
|
||||
};
|
||||
use kernel::prelude::*;
|
||||
|
||||
mod id;
|
||||
mod io;
|
||||
mod irq;
|
||||
|
||||
pub use self::id::{Class, ClassMask, Vendor};
|
||||
pub use self::id::{
|
||||
Class,
|
||||
ClassMask,
|
||||
Vendor, //
|
||||
};
|
||||
pub use self::io::Bar;
|
||||
pub use self::irq::{
|
||||
IrqType,
|
||||
IrqTypes,
|
||||
IrqVector, //
|
||||
};
|
||||
|
||||
/// An adapter for the registration of PCI drivers.
|
||||
pub struct Adapter<T: Driver>(T);
|
||||
@@ -78,9 +97,9 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
let info = T::ID_TABLE.info(id.index());
|
||||
|
||||
from_result(|| {
|
||||
let data = T::probe(pdev, info)?;
|
||||
let data = T::probe(pdev, info);
|
||||
|
||||
pdev.as_ref().set_drvdata(data);
|
||||
pdev.as_ref().set_drvdata(data)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
@@ -95,7 +114,7 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
// SAFETY: `remove_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
let data = unsafe { pdev.as_ref().drvdata_obtain::<Pin<KBox<T>>>() };
|
||||
let data = unsafe { pdev.as_ref().drvdata_obtain::<T>() };
|
||||
|
||||
T::unbind(pdev, data.as_ref());
|
||||
}
|
||||
@@ -249,7 +268,7 @@ macro_rules! pci_device_table {
|
||||
/// fn probe(
|
||||
/// _pdev: &pci::Device<Core>,
|
||||
/// _id_info: &Self::IdInfo,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// Err(ENODEV)
|
||||
/// }
|
||||
/// }
|
||||
@@ -272,7 +291,7 @@ pub trait Driver: Send {
|
||||
///
|
||||
/// Called when a new pci device is added or discovered. Implementers should
|
||||
/// attempt to initialize the device here.
|
||||
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
|
||||
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
|
||||
|
||||
/// PCI driver unbind.
|
||||
///
|
||||
@@ -305,112 +324,6 @@ pub struct Device<Ctx: device::DeviceContext = device::Normal>(
|
||||
PhantomData<Ctx>,
|
||||
);
|
||||
|
||||
/// A PCI BAR to perform I/O-Operations on.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O
|
||||
/// memory mapped PCI bar and its size.
|
||||
pub struct Bar<const SIZE: usize = 0> {
|
||||
pdev: ARef<Device>,
|
||||
io: IoRaw<SIZE>,
|
||||
num: i32,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Bar<SIZE> {
|
||||
fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
|
||||
let len = pdev.resource_len(num)?;
|
||||
if len == 0 {
|
||||
return Err(ENOMEM);
|
||||
}
|
||||
|
||||
// Convert to `i32`, since that's what all the C bindings use.
|
||||
let num = i32::try_from(num)?;
|
||||
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
// `name` is always valid.
|
||||
let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) };
|
||||
if ret != 0 {
|
||||
return Err(EBUSY);
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
// `name` is always valid.
|
||||
let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
|
||||
if ioptr == 0 {
|
||||
// SAFETY:
|
||||
// `pdev` valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
unsafe { bindings::pci_release_region(pdev.as_raw(), num) };
|
||||
return Err(ENOMEM);
|
||||
}
|
||||
|
||||
let io = match IoRaw::new(ioptr, len as usize) {
|
||||
Ok(io) => io,
|
||||
Err(err) => {
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
unsafe { Self::do_release(pdev, ioptr, num) };
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Bar {
|
||||
pdev: pdev.into(),
|
||||
io,
|
||||
num,
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`.
|
||||
unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `ioptr` is valid by the safety requirements.
|
||||
// `num` is valid by the safety requirements.
|
||||
unsafe {
|
||||
bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void);
|
||||
bindings::pci_release_region(pdev.as_raw(), num);
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
// SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
|
||||
unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
#[inline]
|
||||
fn index_is_valid(index: u32) -> bool {
|
||||
// A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
|
||||
index < bindings::PCI_NUM_RESOURCES
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Drop for Bar<SIZE> {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Deref for Bar<SIZE> {
|
||||
type Target = Io<SIZE>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
|
||||
unsafe { Io::from_raw(&self.io) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> Device<Ctx> {
|
||||
#[inline]
|
||||
fn as_raw(&self) -> *mut bindings::pci_dev {
|
||||
@@ -484,7 +397,7 @@ impl Device {
|
||||
unsafe { (*self.as_raw()).subsystem_device }
|
||||
}
|
||||
|
||||
/// Returns the start of the given PCI bar resource.
|
||||
/// Returns the start of the given PCI BAR resource.
|
||||
pub fn resource_start(&self, bar: u32) -> Result<bindings::resource_size_t> {
|
||||
if !Bar::index_is_valid(bar) {
|
||||
return Err(EINVAL);
|
||||
@@ -496,7 +409,7 @@ impl Device {
|
||||
Ok(unsafe { bindings::pci_resource_start(self.as_raw(), bar.try_into()?) })
|
||||
}
|
||||
|
||||
/// Returns the size of the given PCI bar resource.
|
||||
/// Returns the size of the given PCI BAR resource.
|
||||
pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
|
||||
if !Bar::index_is_valid(bar) {
|
||||
return Err(EINVAL);
|
||||
@@ -516,68 +429,6 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<device::Bound> {
|
||||
/// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
|
||||
/// can be performed on compile time for offsets (plus the requested type size) < SIZE.
|
||||
pub fn iomap_region_sized<'a, const SIZE: usize>(
|
||||
&'a self,
|
||||
bar: u32,
|
||||
name: &'a CStr,
|
||||
) -> impl PinInit<Devres<Bar<SIZE>>, Error> + 'a {
|
||||
Devres::new(self.as_ref(), Bar::<SIZE>::new(self, bar, name))
|
||||
}
|
||||
|
||||
/// Mapps an entire PCI-BAR after performing a region-request on it.
|
||||
pub fn iomap_region<'a>(
|
||||
&'a self,
|
||||
bar: u32,
|
||||
name: &'a CStr,
|
||||
) -> impl PinInit<Devres<Bar>, Error> + 'a {
|
||||
self.iomap_region_sized::<0>(bar, name)
|
||||
}
|
||||
|
||||
/// Returns an [`IrqRequest`] for the IRQ vector at the given index, if any.
|
||||
pub fn irq_vector(&self, index: u32) -> Result<IrqRequest<'_>> {
|
||||
// SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`.
|
||||
let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), index) };
|
||||
if irq < 0 {
|
||||
return Err(crate::error::Error::from_errno(irq));
|
||||
}
|
||||
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
||||
Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
|
||||
}
|
||||
|
||||
/// Returns a [`kernel::irq::Registration`] for the IRQ vector at the given
|
||||
/// index.
|
||||
pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
|
||||
&'a self,
|
||||
index: u32,
|
||||
flags: irq::Flags,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> {
|
||||
let request = self.irq_vector(index)?;
|
||||
|
||||
Ok(irq::Registration::<T>::new(request, flags, name, handler))
|
||||
}
|
||||
|
||||
/// Returns a [`kernel::irq::ThreadedRegistration`] for the IRQ vector at
|
||||
/// the given index.
|
||||
pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
|
||||
&'a self,
|
||||
index: u32,
|
||||
flags: irq::Flags,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> {
|
||||
let request = self.irq_vector(index)?;
|
||||
|
||||
Ok(irq::ThreadedRegistration::<T>::new(
|
||||
request, flags, name, handler,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<device::Core> {
|
||||
/// Enable memory resources for this device.
|
||||
pub fn enable_device_mem(&self) -> Result {
|
||||
@@ -593,6 +444,12 @@ impl Device<device::Core> {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `pci::Device` is a transparent wrapper of `struct pci_dev`.
|
||||
// The offset is guaranteed to point to a valid device field inside `pci::Device`.
|
||||
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
|
||||
const OFFSET: usize = offset_of!(bindings::pci_dev, dev);
|
||||
}
|
||||
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
//!
|
||||
//! This module contains PCI class codes, Vendor IDs, and supporting types.
|
||||
|
||||
use crate::{bindings, error::code::EINVAL, error::Error, fmt, prelude::*};
|
||||
use crate::{
|
||||
bindings,
|
||||
fmt,
|
||||
prelude::*, //
|
||||
};
|
||||
|
||||
/// PCI device class codes.
|
||||
///
|
||||
|
||||
144
rust/kernel/pci/io.rs
Normal file
144
rust/kernel/pci/io.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! PCI memory-mapped I/O infrastructure.
|
||||
|
||||
use super::Device;
|
||||
use crate::{
|
||||
bindings,
|
||||
device,
|
||||
devres::Devres,
|
||||
io::{
|
||||
Io,
|
||||
IoRaw, //
|
||||
},
|
||||
prelude::*,
|
||||
sync::aref::ARef, //
|
||||
};
|
||||
use core::ops::Deref;
|
||||
|
||||
/// A PCI BAR to perform I/O-Operations on.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O
|
||||
/// memory mapped PCI BAR and its size.
|
||||
pub struct Bar<const SIZE: usize = 0> {
|
||||
pdev: ARef<Device>,
|
||||
io: IoRaw<SIZE>,
|
||||
num: i32,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Bar<SIZE> {
|
||||
pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
|
||||
let len = pdev.resource_len(num)?;
|
||||
if len == 0 {
|
||||
return Err(ENOMEM);
|
||||
}
|
||||
|
||||
// Convert to `i32`, since that's what all the C bindings use.
|
||||
let num = i32::try_from(num)?;
|
||||
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
// `name` is always valid.
|
||||
let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) };
|
||||
if ret != 0 {
|
||||
return Err(EBUSY);
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
// `name` is always valid.
|
||||
let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
|
||||
if ioptr == 0 {
|
||||
// SAFETY:
|
||||
// `pdev` valid by the invariants of `Device`.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
unsafe { bindings::pci_release_region(pdev.as_raw(), num) };
|
||||
return Err(ENOMEM);
|
||||
}
|
||||
|
||||
let io = match IoRaw::new(ioptr, len as usize) {
|
||||
Ok(io) => io,
|
||||
Err(err) => {
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
|
||||
// `num` is checked for validity by a previous call to `Device::resource_len`.
|
||||
unsafe { Self::do_release(pdev, ioptr, num) };
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Bar {
|
||||
pdev: pdev.into(),
|
||||
io,
|
||||
num,
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `ioptr` must be a valid pointer to the memory mapped PCI BAR number `num`.
|
||||
unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
|
||||
// SAFETY:
|
||||
// `pdev` is valid by the invariants of `Device`.
|
||||
// `ioptr` is valid by the safety requirements.
|
||||
// `num` is valid by the safety requirements.
|
||||
unsafe {
|
||||
bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void);
|
||||
bindings::pci_release_region(pdev.as_raw(), num);
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
// SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
|
||||
unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
#[inline]
|
||||
pub(super) fn index_is_valid(index: u32) -> bool {
|
||||
// A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
|
||||
index < bindings::PCI_NUM_RESOURCES
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Drop for Bar<SIZE> {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Deref for Bar<SIZE> {
|
||||
type Target = Io<SIZE>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
|
||||
unsafe { Io::from_raw(&self.io) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<device::Bound> {
|
||||
/// Maps an entire PCI BAR after performing a region-request on it. I/O operation bound checks
|
||||
/// can be performed on compile time for offsets (plus the requested type size) < SIZE.
|
||||
pub fn iomap_region_sized<'a, const SIZE: usize>(
|
||||
&'a self,
|
||||
bar: u32,
|
||||
name: &'a CStr,
|
||||
) -> impl PinInit<Devres<Bar<SIZE>>, Error> + 'a {
|
||||
Devres::new(self.as_ref(), Bar::<SIZE>::new(self, bar, name))
|
||||
}
|
||||
|
||||
/// Maps an entire PCI BAR after performing a region-request on it.
|
||||
pub fn iomap_region<'a>(
|
||||
&'a self,
|
||||
bar: u32,
|
||||
name: &'a CStr,
|
||||
) -> impl PinInit<Devres<Bar>, Error> + 'a {
|
||||
self.iomap_region_sized::<0>(bar, name)
|
||||
}
|
||||
}
|
||||
252
rust/kernel/pci/irq.rs
Normal file
252
rust/kernel/pci/irq.rs
Normal file
@@ -0,0 +1,252 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! PCI interrupt infrastructure.
|
||||
|
||||
use super::Device;
|
||||
use crate::{
|
||||
bindings,
|
||||
device,
|
||||
device::Bound,
|
||||
devres,
|
||||
error::to_result,
|
||||
irq::{
|
||||
self,
|
||||
IrqRequest, //
|
||||
},
|
||||
prelude::*,
|
||||
str::CStr,
|
||||
sync::aref::ARef, //
|
||||
};
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
/// IRQ type flags for PCI interrupt allocation.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum IrqType {
|
||||
/// INTx interrupts.
|
||||
Intx,
|
||||
/// Message Signaled Interrupts (MSI).
|
||||
Msi,
|
||||
/// Extended Message Signaled Interrupts (MSI-X).
|
||||
MsiX,
|
||||
}
|
||||
|
||||
impl IrqType {
|
||||
/// Convert to the corresponding kernel flags.
|
||||
const fn as_raw(self) -> u32 {
|
||||
match self {
|
||||
IrqType::Intx => bindings::PCI_IRQ_INTX,
|
||||
IrqType::Msi => bindings::PCI_IRQ_MSI,
|
||||
IrqType::MsiX => bindings::PCI_IRQ_MSIX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of IRQ types that can be used for PCI interrupt allocation.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct IrqTypes(u32);
|
||||
|
||||
impl IrqTypes {
|
||||
/// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
|
||||
pub const fn all() -> Self {
|
||||
Self(bindings::PCI_IRQ_ALL_TYPES)
|
||||
}
|
||||
|
||||
/// Build a set of IRQ types.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Create a set with only MSI and MSI-X (no INTx interrupts).
|
||||
/// let msi_only = IrqTypes::default()
|
||||
/// .with(IrqType::Msi)
|
||||
/// .with(IrqType::MsiX);
|
||||
/// ```
|
||||
pub const fn with(self, irq_type: IrqType) -> Self {
|
||||
Self(self.0 | irq_type.as_raw())
|
||||
}
|
||||
|
||||
/// Get the raw flags value.
|
||||
const fn as_raw(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an allocated IRQ vector for a specific PCI device.
|
||||
///
|
||||
/// This type ties an IRQ vector to the device it was allocated for,
|
||||
/// ensuring the vector is only used with the correct device.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IrqVector<'a> {
|
||||
dev: &'a Device<Bound>,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
impl<'a> IrqVector<'a> {
|
||||
/// Creates a new [`IrqVector`] for the given device and index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `index` must be a valid IRQ vector index for `dev`.
|
||||
/// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors.
|
||||
unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self {
|
||||
Self { dev, index }
|
||||
}
|
||||
|
||||
/// Returns the raw vector index.
|
||||
fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_into(self) -> Result<IrqRequest<'a>> {
|
||||
// SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`.
|
||||
let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) };
|
||||
if irq < 0 {
|
||||
return Err(crate::error::Error::from_errno(irq));
|
||||
}
|
||||
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
||||
Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an IRQ vector allocation for a PCI device.
|
||||
///
|
||||
/// This type ensures that IRQ vectors are properly allocated and freed by
|
||||
/// tying the allocation to the lifetime of this registration object.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// The [`Device`] has successfully allocated IRQ vectors.
|
||||
struct IrqVectorRegistration {
|
||||
dev: ARef<Device>,
|
||||
}
|
||||
|
||||
impl IrqVectorRegistration {
|
||||
/// Allocate and register IRQ vectors for the given PCI device.
|
||||
///
|
||||
/// Allocates IRQ vectors and registers them with devres for automatic cleanup.
|
||||
/// Returns a range of valid IRQ vectors.
|
||||
fn register<'a>(
|
||||
dev: &'a Device<Bound>,
|
||||
min_vecs: u32,
|
||||
max_vecs: u32,
|
||||
irq_types: IrqTypes,
|
||||
) -> Result<RangeInclusive<IrqVector<'a>>> {
|
||||
// SAFETY:
|
||||
// - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
|
||||
// by the type invariant of `Device`.
|
||||
// - `pci_alloc_irq_vectors` internally validates all other parameters
|
||||
// and returns error codes.
|
||||
let ret = unsafe {
|
||||
bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
|
||||
};
|
||||
|
||||
to_result(ret)?;
|
||||
let count = ret as u32;
|
||||
|
||||
// SAFETY:
|
||||
// - `pci_alloc_irq_vectors` returns the number of allocated vectors on success.
|
||||
// - Vectors are 0-based, so valid indices are [0, count-1].
|
||||
// - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and
|
||||
// `count - 1` are valid IRQ vector indices for `dev`.
|
||||
let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };
|
||||
|
||||
// INVARIANT: The IRQ vector allocation for `dev` above was successful.
|
||||
let irq_vecs = Self { dev: dev.into() };
|
||||
devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?;
|
||||
|
||||
Ok(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IrqVectorRegistration {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY:
|
||||
// - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
|
||||
// - `self.dev` has successfully allocated IRQ vectors.
|
||||
unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<device::Bound> {
|
||||
/// Returns a [`kernel::irq::Registration`] for the given IRQ vector.
|
||||
pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
|
||||
&'a self,
|
||||
vector: IrqVector<'a>,
|
||||
flags: irq::Flags,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> impl PinInit<irq::Registration<T>, Error> + 'a {
|
||||
pin_init::pin_init_scope(move || {
|
||||
let request = vector.try_into()?;
|
||||
|
||||
Ok(irq::Registration::<T>::new(request, flags, name, handler))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector.
|
||||
pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
|
||||
&'a self,
|
||||
vector: IrqVector<'a>,
|
||||
flags: irq::Flags,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
|
||||
pin_init::pin_init_scope(move || {
|
||||
let request = vector.try_into()?;
|
||||
|
||||
Ok(irq::ThreadedRegistration::<T>::new(
|
||||
request, flags, name, handler,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Allocate IRQ vectors for this PCI device with automatic cleanup.
|
||||
///
|
||||
/// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
|
||||
/// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
|
||||
/// parameter and hardware capabilities. When multiple types are specified, the kernel
|
||||
/// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
|
||||
///
|
||||
/// The allocated vectors are automatically freed when the device is unbound, using the
|
||||
/// devres (device resource management) system.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `min_vecs` - Minimum number of vectors required.
|
||||
/// * `max_vecs` - Maximum number of vectors to allocate.
|
||||
/// * `irq_types` - Types of interrupts that can be used.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a range of IRQ vectors that were successfully allocated, or an error if the
|
||||
/// allocation fails or cannot meet the minimum requirement.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use kernel::{ device::Bound, pci};
|
||||
/// # fn no_run(dev: &pci::Device<Bound>) -> Result {
|
||||
/// // Allocate using any available interrupt type in the order mentioned above.
|
||||
/// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
|
||||
///
|
||||
/// // Allocate MSI or MSI-X only (no INTx interrupts).
|
||||
/// let msi_only = pci::IrqTypes::default()
|
||||
/// .with(pci::IrqType::Msi)
|
||||
/// .with(pci::IrqType::MsiX);
|
||||
/// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn alloc_irq_vectors(
|
||||
&self,
|
||||
min_vecs: u32,
|
||||
max_vecs: u32,
|
||||
irq_types: IrqTypes,
|
||||
) -> Result<RangeInclusive<IrqVector<'_>>> {
|
||||
IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ use crate::{
|
||||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::offset_of,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
@@ -74,9 +75,9 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
let info = <Self as driver::Adapter>::id_info(pdev.as_ref());
|
||||
|
||||
from_result(|| {
|
||||
let data = T::probe(pdev, info)?;
|
||||
let data = T::probe(pdev, info);
|
||||
|
||||
pdev.as_ref().set_drvdata(data);
|
||||
pdev.as_ref().set_drvdata(data)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
@@ -91,7 +92,7 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
// SAFETY: `remove_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
let data = unsafe { pdev.as_ref().drvdata_obtain::<Pin<KBox<T>>>() };
|
||||
let data = unsafe { pdev.as_ref().drvdata_obtain::<T>() };
|
||||
|
||||
T::unbind(pdev, data.as_ref());
|
||||
}
|
||||
@@ -166,7 +167,7 @@ macro_rules! module_platform_driver {
|
||||
/// fn probe(
|
||||
/// _pdev: &platform::Device<Core>,
|
||||
/// _id_info: Option<&Self::IdInfo>,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// Err(ENODEV)
|
||||
/// }
|
||||
/// }
|
||||
@@ -190,8 +191,10 @@ pub trait Driver: Send {
|
||||
///
|
||||
/// Called when a new platform device is added or discovered.
|
||||
/// Implementers should attempt to initialize the device here.
|
||||
fn probe(dev: &Device<device::Core>, id_info: Option<&Self::IdInfo>)
|
||||
-> Result<Pin<KBox<Self>>>;
|
||||
fn probe(
|
||||
dev: &Device<device::Core>,
|
||||
id_info: Option<&Self::IdInfo>,
|
||||
) -> impl PinInit<Self, Error>;
|
||||
|
||||
/// Platform driver unbind.
|
||||
///
|
||||
@@ -285,6 +288,12 @@ impl Device<Bound> {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `platform::Device` is a transparent wrapper of `struct platform_device`.
|
||||
// The offset is guaranteed to point to a valid device field inside `platform::Device`.
|
||||
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
|
||||
const OFFSET: usize = offset_of!(bindings::platform_device, dev);
|
||||
}
|
||||
|
||||
macro_rules! define_irq_accessor_by_index {
|
||||
(
|
||||
$(#[$meta:meta])* $fn_name:ident,
|
||||
@@ -299,15 +308,17 @@ macro_rules! define_irq_accessor_by_index {
|
||||
index: u32,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> Result<impl PinInit<irq::$reg_type<T>, Error> + 'a> {
|
||||
let request = self.$request_fn(index)?;
|
||||
) -> impl PinInit<irq::$reg_type<T>, Error> + 'a {
|
||||
pin_init::pin_init_scope(move || {
|
||||
let request = self.$request_fn(index)?;
|
||||
|
||||
Ok(irq::$reg_type::<T>::new(
|
||||
request,
|
||||
flags,
|
||||
name,
|
||||
handler,
|
||||
))
|
||||
Ok(irq::$reg_type::<T>::new(
|
||||
request,
|
||||
flags,
|
||||
name,
|
||||
handler,
|
||||
))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -323,18 +334,20 @@ macro_rules! define_irq_accessor_by_name {
|
||||
pub fn $fn_name<'a, T: irq::$handler_trait + 'static>(
|
||||
&'a self,
|
||||
flags: irq::Flags,
|
||||
irq_name: &CStr,
|
||||
irq_name: &'a CStr,
|
||||
name: &'static CStr,
|
||||
handler: impl PinInit<T, Error> + 'a,
|
||||
) -> Result<impl PinInit<irq::$reg_type<T>, Error> + 'a> {
|
||||
let request = self.$request_fn(irq_name)?;
|
||||
) -> impl PinInit<irq::$reg_type<T>, Error> + 'a {
|
||||
pin_init::pin_init_scope(move || {
|
||||
let request = self.$request_fn(irq_name)?;
|
||||
|
||||
Ok(irq::$reg_type::<T>::new(
|
||||
request,
|
||||
flags,
|
||||
name,
|
||||
handler,
|
||||
))
|
||||
Ok(irq::$reg_type::<T>::new(
|
||||
request,
|
||||
flags,
|
||||
name,
|
||||
handler,
|
||||
))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::{
|
||||
device::{Bound, Device},
|
||||
devres::Devres,
|
||||
dma, error,
|
||||
io::resource::ResourceSize,
|
||||
io::ResourceSize,
|
||||
page,
|
||||
prelude::*,
|
||||
types::{ARef, Opaque},
|
||||
|
||||
@@ -47,3 +47,40 @@ pub fn fsleep(delta: Delta) {
|
||||
bindings::fsleep(delta.as_micros_ceil() as c_ulong)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a delay based on microseconds with busy waiting.
|
||||
///
|
||||
/// Equivalent to the C side [`udelay()`], which delays in microseconds.
|
||||
///
|
||||
/// `delta` must be within `[0, MAX_UDELAY_MS]` in milliseconds;
|
||||
/// otherwise, it is erroneous behavior. That is, it is considered a bug to
|
||||
/// call this function with an out-of-range value.
|
||||
///
|
||||
/// The behavior above differs from the C side [`udelay()`] for which out-of-range
|
||||
/// values could lead to an overflow and unexpected behavior.
|
||||
///
|
||||
/// [`udelay()`]: https://docs.kernel.org/timers/delay_sleep_functions.html#c.udelay
|
||||
pub fn udelay(delta: Delta) {
|
||||
const MAX_UDELAY_DELTA: Delta = Delta::from_millis(bindings::MAX_UDELAY_MS as i64);
|
||||
|
||||
debug_assert!(delta.as_nanos() >= 0);
|
||||
debug_assert!(delta <= MAX_UDELAY_DELTA);
|
||||
|
||||
let delta = if (Delta::ZERO..=MAX_UDELAY_DELTA).contains(&delta) {
|
||||
delta
|
||||
} else {
|
||||
MAX_UDELAY_DELTA
|
||||
};
|
||||
|
||||
// SAFETY: It is always safe to call `udelay()` with any duration.
|
||||
// Note that the kernel is compiled with `-fno-strict-overflow`
|
||||
// so any out-of-range value could lead to unexpected behavior
|
||||
// but won't lead to undefined behavior.
|
||||
unsafe {
|
||||
// Convert the duration to microseconds and round up to preserve
|
||||
// the guarantee; `udelay()` inserts a delay for at least
|
||||
// the provided duration, but that it may delay for longer
|
||||
// under some circumstances.
|
||||
bindings::udelay(delta.as_micros_ceil() as c_ulong)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::{
|
||||
bindings,
|
||||
error::Result,
|
||||
ffi::{c_char, c_void},
|
||||
fs::file,
|
||||
prelude::*,
|
||||
transmute::{AsBytes, FromBytes},
|
||||
};
|
||||
@@ -287,6 +288,48 @@ impl UserSliceReader {
|
||||
self.read_raw(out)
|
||||
}
|
||||
|
||||
/// Reads raw data from the user slice into a kernel buffer partially.
|
||||
///
|
||||
/// This is the same as [`Self::read_slice`] but considers the given `offset` into `out` and
|
||||
/// truncates the read to the boundaries of `self` and `out`.
|
||||
///
|
||||
/// On success, returns the number of bytes read.
|
||||
pub fn read_slice_partial(&mut self, out: &mut [u8], offset: usize) -> Result<usize> {
|
||||
let end = offset.saturating_add(self.len()).min(out.len());
|
||||
|
||||
let Some(dst) = out.get_mut(offset..end) else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
self.read_slice(dst)?;
|
||||
Ok(dst.len())
|
||||
}
|
||||
|
||||
/// Reads raw data from the user slice into a kernel buffer partially.
|
||||
///
|
||||
/// This is the same as [`Self::read_slice_partial`] but updates the given [`file::Offset`] by
|
||||
/// the number of bytes read.
|
||||
///
|
||||
/// This is equivalent to C's `simple_write_to_buffer()`.
|
||||
///
|
||||
/// On success, returns the number of bytes read.
|
||||
pub fn read_slice_file(&mut self, out: &mut [u8], offset: &mut file::Offset) -> Result<usize> {
|
||||
if offset.is_negative() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
let Ok(offset_index) = (*offset).try_into() else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let read = self.read_slice_partial(out, offset_index)?;
|
||||
|
||||
// OVERFLOW: `offset + read <= data.len() <= isize::MAX <= Offset::MAX`
|
||||
*offset += read as i64;
|
||||
|
||||
Ok(read)
|
||||
}
|
||||
|
||||
/// Reads a value of the specified type.
|
||||
///
|
||||
/// Fails with [`EFAULT`] if the read happens on a bad address, or if the read goes out of
|
||||
@@ -438,6 +481,48 @@ impl UserSliceWriter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes raw data to this user pointer from a kernel buffer partially.
|
||||
///
|
||||
/// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and
|
||||
/// truncates the write to the boundaries of `self` and `data`.
|
||||
///
|
||||
/// On success, returns the number of bytes written.
|
||||
pub fn write_slice_partial(&mut self, data: &[u8], offset: usize) -> Result<usize> {
|
||||
let end = offset.saturating_add(self.len()).min(data.len());
|
||||
|
||||
let Some(src) = data.get(offset..end) else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
self.write_slice(src)?;
|
||||
Ok(src.len())
|
||||
}
|
||||
|
||||
/// Writes raw data to this user pointer from a kernel buffer partially.
|
||||
///
|
||||
/// This is the same as [`Self::write_slice_partial`] but updates the given [`file::Offset`] by
|
||||
/// the number of bytes written.
|
||||
///
|
||||
/// This is equivalent to C's `simple_read_from_buffer()`.
|
||||
///
|
||||
/// On success, returns the number of bytes written.
|
||||
pub fn write_slice_file(&mut self, data: &[u8], offset: &mut file::Offset) -> Result<usize> {
|
||||
if offset.is_negative() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
let Ok(offset_index) = (*offset).try_into() else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let written = self.write_slice_partial(data, offset_index)?;
|
||||
|
||||
// OVERFLOW: `offset + written <= data.len() <= isize::MAX <= Offset::MAX`
|
||||
*offset += written as i64;
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
/// Writes the provided Rust value to this userspace pointer.
|
||||
///
|
||||
/// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of
|
||||
|
||||
@@ -15,7 +15,14 @@ use crate::{
|
||||
types::{AlwaysRefCounted, Opaque},
|
||||
ThisModule,
|
||||
};
|
||||
use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::{
|
||||
offset_of,
|
||||
MaybeUninit, //
|
||||
},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
/// An adapter for the registration of USB drivers.
|
||||
pub struct Adapter<T: Driver>(T);
|
||||
@@ -67,10 +74,10 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
let id = unsafe { &*id.cast::<DeviceId>() };
|
||||
|
||||
let info = T::ID_TABLE.info(id.index());
|
||||
let data = T::probe(intf, id, info)?;
|
||||
let data = T::probe(intf, id, info);
|
||||
|
||||
let dev: &device::Device<device::CoreInternal> = intf.as_ref();
|
||||
dev.set_drvdata(data);
|
||||
dev.set_drvdata(data)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
@@ -87,7 +94,7 @@ impl<T: Driver + 'static> Adapter<T> {
|
||||
// SAFETY: `disconnect_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
||||
// and stored a `Pin<KBox<T>>`.
|
||||
let data = unsafe { dev.drvdata_obtain::<Pin<KBox<T>>>() };
|
||||
let data = unsafe { dev.drvdata_obtain::<T>() };
|
||||
|
||||
T::disconnect(intf, data.as_ref());
|
||||
}
|
||||
@@ -270,7 +277,7 @@ macro_rules! usb_device_table {
|
||||
/// _interface: &usb::Interface<Core>,
|
||||
/// _id: &usb::DeviceId,
|
||||
/// _info: &Self::IdInfo,
|
||||
/// ) -> Result<Pin<KBox<Self>>> {
|
||||
/// ) -> impl PinInit<Self, Error> {
|
||||
/// Err(ENODEV)
|
||||
/// }
|
||||
///
|
||||
@@ -292,7 +299,7 @@ pub trait Driver {
|
||||
interface: &Interface<device::Core>,
|
||||
id: &DeviceId,
|
||||
id_info: &Self::IdInfo,
|
||||
) -> Result<Pin<KBox<Self>>>;
|
||||
) -> impl PinInit<Self, Error>;
|
||||
|
||||
/// USB driver disconnect.
|
||||
///
|
||||
@@ -324,6 +331,12 @@ impl<Ctx: device::DeviceContext> Interface<Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `usb::Interface` is a transparent wrapper of `struct usb_interface`.
|
||||
// The offset is guaranteed to point to a valid device field inside `usb::Interface`.
|
||||
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Interface<Ctx> {
|
||||
const OFFSET: usize = offset_of!(bindings::usb_interface, dev);
|
||||
}
|
||||
|
||||
// SAFETY: `Interface` is a transparent wrapper of a type that doesn't depend on
|
||||
// `Interface`'s generic argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Interface });
|
||||
|
||||
@@ -1392,6 +1392,93 @@ where
|
||||
unsafe { pin_init_from_closure(init) }
|
||||
}
|
||||
|
||||
/// Construct an initializer in a closure and run it.
|
||||
///
|
||||
/// Returns an initializer that first runs the closure and then the initializer returned by it.
|
||||
///
|
||||
/// See also [`init_scope`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pin_init::*;
|
||||
/// # #[pin_data]
|
||||
/// # struct Foo { a: u64, b: isize }
|
||||
/// # struct Bar { a: u32, b: isize }
|
||||
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
|
||||
/// # struct Error;
|
||||
/// fn init_foo() -> impl PinInit<Foo, Error> {
|
||||
/// pin_init_scope(|| {
|
||||
/// let bar = lookup_bar()?;
|
||||
/// Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
|
||||
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
|
||||
/// initializer returned by the [`try_pin_init!`] invocation.
|
||||
pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<I, E>,
|
||||
I: PinInit<T, E>,
|
||||
{
|
||||
// SAFETY:
|
||||
// - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
|
||||
// - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__pinned_init`.
|
||||
// - The safety requirements of `init.__pinned_init` are fulfilled, since it's being called
|
||||
// from an initializer.
|
||||
unsafe {
|
||||
pin_init_from_closure(move |slot: *mut T| -> Result<(), E> {
|
||||
let init = make_init()?;
|
||||
init.__pinned_init(slot)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct an initializer in a closure and run it.
|
||||
///
|
||||
/// Returns an initializer that first runs the closure and then the initializer returned by it.
|
||||
///
|
||||
/// See also [`pin_init_scope`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pin_init::*;
|
||||
/// # struct Foo { a: u64, b: isize }
|
||||
/// # struct Bar { a: u32, b: isize }
|
||||
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
|
||||
/// # struct Error;
|
||||
/// fn init_foo() -> impl Init<Foo, Error> {
|
||||
/// init_scope(|| {
|
||||
/// let bar = lookup_bar()?;
|
||||
/// Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
|
||||
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
|
||||
/// initializer returned by the [`try_init!`] invocation.
|
||||
pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<I, E>,
|
||||
I: Init<T, E>,
|
||||
{
|
||||
// SAFETY:
|
||||
// - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
|
||||
// - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__init`.
|
||||
// - The safety requirements of `init.__init` are fulfilled, since it's being called from an
|
||||
// initializer.
|
||||
unsafe {
|
||||
init_from_closure(move |slot: *mut T| -> Result<(), E> {
|
||||
let init = make_init()?;
|
||||
init.__init(slot)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
|
||||
unsafe impl<T> Init<T> for T {
|
||||
unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
Reference in New Issue
Block a user