at master 3.9 kB view raw
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 c_str, 73 device, 74 devres::Devres, 75 i2c, 76 of, 77 platform, 78 prelude::*, 79 sync::aref::ARef, // 80}; 81 82#[pin_data] 83struct SampleDriver { 84 parent_dev: ARef<platform::Device>, 85 #[pin] 86 _reg: Devres<i2c::Registration>, 87} 88 89kernel::of_device_table!( 90 OF_TABLE, 91 MODULE_OF_TABLE, 92 <SampleDriver as platform::Driver>::IdInfo, 93 [(of::DeviceId::new(c_str!("test,rust-device")), ())] 94); 95 96kernel::acpi_device_table!( 97 ACPI_TABLE, 98 MODULE_ACPI_TABLE, 99 <SampleDriver as platform::Driver>::IdInfo, 100 [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] 101); 102 103const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30; 104const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0; 105const BOARD_INFO: i2c::I2cBoardInfo = 106 i2c::I2cBoardInfo::new(c_str!("rust_driver_i2c"), SAMPLE_I2C_CLIENT_ADDR); 107 108impl platform::Driver for SampleDriver { 109 type IdInfo = (); 110 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); 111 const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); 112 113 fn probe( 114 pdev: &platform::Device<device::Core>, 115 _info: Option<&Self::IdInfo>, 116 ) -> impl PinInit<Self, Error> { 117 dev_info!( 118 pdev.as_ref(), 119 "Probe Rust I2C Client registration sample.\n" 120 ); 121 122 kernel::try_pin_init!( Self { 123 parent_dev: pdev.into(), 124 125 _reg <- { 126 let adapter = i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?; 127 128 i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref()) 129 } 130 }) 131 } 132 133 fn unbind(pdev: &platform::Device<device::Core>, _this: Pin<&Self>) { 134 dev_info!( 135 pdev.as_ref(), 136 "Unbind Rust I2C Client registration sample.\n" 137 ); 138 } 139} 140 141kernel::module_platform_driver! { 142 type: SampleDriver, 143 name: "rust_device_i2c", 144 authors: ["Danilo Krummrich", "Igor Korotin"], 145 description: "Rust I2C client registration", 146 license: "GPL v2", 147}