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 70 use 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] 82 struct SampleDriver { 83 parent_dev: ARef<platform::Device>, 84 #[pin] 85 _reg: Devres<i2c::Registration>, 86 } 87 88 kernel::of_device_table!( 89 OF_TABLE, 90 <SampleDriver as platform::Driver>::IdInfo, 91 [(of::DeviceId::new(c"test,rust-device"), ())] 92 ); 93 94 kernel::acpi_device_table!( 95 ACPI_TABLE, 96 <SampleDriver as platform::Driver>::IdInfo, 97 [(acpi::DeviceId::new(c"LNUXBEEF"), ())] 98 ); 99 100 const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30; 101 const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0; 102 const BOARD_INFO: i2c::I2cBoardInfo = 103 i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR); 104 105 impl 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 142 kernel::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 } 149