projects/linux·blob master · refs

148 lines · 3.9 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3//! Rust I2C client registration sample.
4//!
5//! An I2C client in Rust cannot exist on its own. To register a new I2C client,
6//! it must be bound to a parent device. In this sample driver, a platform device
7//! is used as the parent.
8//!
9
10//! ACPI match table test
11//!
12//! This demonstrates how to test an ACPI-based Rust I2C client registration driver
13//! using QEMU with a custom SSDT.
14//!
15//! Steps:
16//!
17//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
18//!
19//! ```asl
20//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
21//! {
22//! Scope (\_SB)
23//! {
24//! Device (T432)
25//! {
26//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match
27//! Name (_UID, 1)
28//! Name (_STA, 0x0F) // Device present, enabled
29//! Name (_CRS, ResourceTemplate ()
30//! {
31//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
32//! })
33//! }
34//! }
35//! }
36//! ```
37//!
38//! 2. **Compile the table**:
39//!
40//! ```sh
41//! iasl -tc ssdt.dsl
42//! ```
43//!
44//! This generates `ssdt.aml`
45//!
46//! 3. **Run QEMU** with the compiled AML file:
47//!
48//! ```sh
49//! qemu-system-x86_64 -m 512M \
50//! -enable-kvm \
51//! -kernel path/to/bzImage \
52//! -append "root=/dev/sda console=ttyS0" \
53//! -hda rootfs.img \
54//! -serial stdio \
55//! -acpitable file=ssdt.aml
56//! ```
57//!
58//! Requirements:
59//! - The `rust_driver_platform` must be present either:
60//! - built directly into the kernel (`bzImage`), or
61//! - available as a `.ko` file and loadable from `rootfs.img`
62//!
63//! 4. **Verify it worked** by checking `dmesg`:
64//!
65//! ```
66//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'.
67//! ```
68//!
69
70use kernel::{
71 acpi,
72 device,
73 devres::Devres,
74 i2c,
75 of,
76 platform,
77 prelude::*,
78 sync::aref::ARef, //
79};
80
81#[pin_data]
82struct SampleDriver {
83 parent_dev: ARef<platform::Device>,
84 #[pin]
85 _reg: Devres<i2c::Registration>,
86}
87
88kernel::of_device_table!(
89 OF_TABLE,
90 <SampleDriver as platform::Driver>::IdInfo,
91 [(of::DeviceId::new(c"test,rust-device"), ())]
92);
93
94kernel::acpi_device_table!(
95 ACPI_TABLE,
96 <SampleDriver as platform::Driver>::IdInfo,
97 [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
98);
99
100const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30;
101const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0;
102const BOARD_INFO: i2c::I2cBoardInfo =
103 i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR);
104
105impl platform::Driver for SampleDriver {
106 type IdInfo = ();
107 type Data<'bound> = Self;
108 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
109 const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
110
111 fn probe<'bound>(
112 pdev: &'bound platform::Device<device::Core<'_>>,
113 _info: Option<&'bound Self::IdInfo>,
114 ) -> impl PinInit<Self, Error> + 'bound {
115 dev_info!(
116 pdev.as_ref(),
117 "Probe Rust I2C Client registration sample.\n"
118 );
119
120 kernel::try_pin_init!( Self {
121 parent_dev: pdev.into(),
122
123 _reg <- {
124 let adapter = i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?;
125
126 i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref())
127 }
128 })
129 }
130
131 fn unbind<'bound>(
132 pdev: &'bound platform::Device<device::Core<'_>>,
133 _this: Pin<&Self::Data<'bound>>,
134 ) {
135 dev_info!(
136 pdev.as_ref(),
137 "Unbind Rust I2C Client registration sample.\n"
138 );
139 }
140}
141
142kernel::module_platform_driver! {
143 type: SampleDriver,
144 name: "rust_device_i2c",
145 authors: ["Danilo Krummrich", "Igor Korotin"],
146 description: "Rust I2C client registration",
147 license: "GPL v2",
148}