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 c_str, 73 device, 74 devres::Devres, 75 i2c, 76 of, 77 platform, 78 prelude::*, 79 sync::aref::ARef, // 80 }; 81 82 #[pin_data] 83 struct SampleDriver { 84 parent_dev: ARef<platform::Device>, 85 #[pin] 86 _reg: Devres<i2c::Registration>, 87 } 88 89 kernel::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 96 kernel::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 103 const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30; 104 const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0; 105 const BOARD_INFO: i2c::I2cBoardInfo = 106 i2c::I2cBoardInfo::new(c_str!("rust_driver_i2c"), SAMPLE_I2C_CLIENT_ADDR); 107 108 impl 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 probe( pdev: &platform::Device<device::Core>, _info: Option<&Self::IdInfo>, ) -> impl PinInit<Self, Error>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 unbind(pdev: &platform::Device<device::Core>, _this: Pin<&Self>)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 141 kernel::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 } 148