rust_driver_platform.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // SPDX-License-Identifier: GPL-2.0
  2. //! Rust Platform driver sample.
  3. //! ACPI match table test
  4. //!
  5. //! This demonstrates how to test an ACPI-based Rust platform driver using QEMU
  6. //! with a custom SSDT.
  7. //!
  8. //! Steps:
  9. //!
  10. //! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
  11. //!
  12. //! ```asl
  13. //! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
  14. //! {
  15. //! Scope (\_SB)
  16. //! {
  17. //! Device (T432)
  18. //! {
  19. //! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match
  20. //! Name (_UID, 1)
  21. //! Name (_STA, 0x0F) // Device present, enabled
  22. //! Name (_CRS, ResourceTemplate ()
  23. //! {
  24. //! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
  25. //! })
  26. //! }
  27. //! }
  28. //! }
  29. //! ```
  30. //!
  31. //! 2. **Compile the table**:
  32. //!
  33. //! ```sh
  34. //! iasl -tc ssdt.dsl
  35. //! ```
  36. //!
  37. //! This generates `ssdt.aml`
  38. //!
  39. //! 3. **Run QEMU** with the compiled AML file:
  40. //!
  41. //! ```sh
  42. //! qemu-system-x86_64 -m 512M \
  43. //! -enable-kvm \
  44. //! -kernel path/to/bzImage \
  45. //! -append "root=/dev/sda console=ttyS0" \
  46. //! -hda rootfs.img \
  47. //! -serial stdio \
  48. //! -acpitable file=ssdt.aml
  49. //! ```
  50. //!
  51. //! Requirements:
  52. //! - The `rust_driver_platform` must be present either:
  53. //! - built directly into the kernel (`bzImage`), or
  54. //! - available as a `.ko` file and loadable from `rootfs.img`
  55. //!
  56. //! 4. **Verify it worked** by checking `dmesg`:
  57. //!
  58. //! ```
  59. //! rust_driver_platform LNUXBEEF:00: Probed with info: '0'.
  60. //! ```
  61. //!
  62. use kernel::{
  63. acpi,
  64. device::{
  65. self,
  66. property::{
  67. FwNodeReferenceArgs,
  68. NArgs, //
  69. },
  70. Core,
  71. },
  72. of,
  73. platform,
  74. prelude::*,
  75. str::CString,
  76. sync::aref::ARef, //
  77. };
  78. struct SampleDriver {
  79. pdev: ARef<platform::Device>,
  80. }
  81. struct Info(u32);
  82. kernel::of_device_table!(
  83. OF_TABLE,
  84. MODULE_OF_TABLE,
  85. <SampleDriver as platform::Driver>::IdInfo,
  86. [(of::DeviceId::new(c"test,rust-device"), Info(42))]
  87. );
  88. kernel::acpi_device_table!(
  89. ACPI_TABLE,
  90. MODULE_ACPI_TABLE,
  91. <SampleDriver as platform::Driver>::IdInfo,
  92. [(acpi::DeviceId::new(c"LNUXBEEF"), Info(0))]
  93. );
  94. impl platform::Driver for SampleDriver {
  95. type IdInfo = Info;
  96. const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
  97. const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
  98. fn probe(
  99. pdev: &platform::Device<Core>,
  100. info: Option<&Self::IdInfo>,
  101. ) -> impl PinInit<Self, Error> {
  102. let dev = pdev.as_ref();
  103. dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
  104. if let Some(info) = info {
  105. dev_info!(dev, "Probed with info: '{}'.\n", info.0);
  106. }
  107. if dev.fwnode().is_some_and(|node| node.is_of_node()) {
  108. Self::properties_parse(dev)?;
  109. }
  110. Ok(Self { pdev: pdev.into() })
  111. }
  112. }
  113. impl SampleDriver {
  114. fn properties_parse(dev: &device::Device) -> Result {
  115. let fwnode = dev.fwnode().ok_or(ENOENT)?;
  116. if let Ok(idx) = fwnode.property_match_string(c"compatible", c"test,rust-device") {
  117. dev_info!(dev, "matched compatible string idx = {}\n", idx);
  118. }
  119. let name = c"compatible";
  120. let prop = fwnode.property_read::<CString>(name).required_by(dev)?;
  121. dev_info!(dev, "'{name}'='{prop:?}'\n");
  122. let name = c"test,bool-prop";
  123. let prop = fwnode.property_read_bool(c"test,bool-prop");
  124. dev_info!(dev, "'{name}'='{prop}'\n");
  125. if fwnode.property_present(c"test,u32-prop") {
  126. dev_info!(dev, "'test,u32-prop' is present\n");
  127. }
  128. let name = c"test,u32-optional-prop";
  129. let prop = fwnode.property_read::<u32>(name).or(0x12);
  130. dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n");
  131. // A missing required property will print an error. Discard the error to
  132. // prevent properties_parse from failing in that case.
  133. let name = c"test,u32-required-prop";
  134. let _ = fwnode.property_read::<u32>(name).required_by(dev);
  135. let name = c"test,u32-prop";
  136. let prop: u32 = fwnode.property_read(name).required_by(dev)?;
  137. dev_info!(dev, "'{name}'='{prop:#x}'\n");
  138. let name = c"test,i16-array";
  139. let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?;
  140. dev_info!(dev, "'{name}'='{prop:?}'\n");
  141. let len = fwnode.property_count_elem::<u16>(name)?;
  142. dev_info!(dev, "'{name}' length is {len}\n");
  143. let name = c"test,i16-array";
  144. let prop: KVec<i16> = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?;
  145. dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n");
  146. for child in fwnode.children() {
  147. let name = c"test,ref-arg";
  148. let nargs = NArgs::N(2);
  149. let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?;
  150. dev_info!(dev, "'{name}'='{prop:?}'\n");
  151. }
  152. Ok(())
  153. }
  154. }
  155. impl Drop for SampleDriver {
  156. fn drop(&mut self) {
  157. dev_dbg!(self.pdev, "Remove Rust Platform driver sample.\n");
  158. }
  159. }
  160. kernel::module_platform_driver! {
  161. type: SampleDriver,
  162. name: "rust_driver_platform",
  163. authors: ["Danilo Krummrich"],
  164. description: "Rust Platform driver",
  165. license: "GPL v2",
  166. }