1 // SPDX-License-Identifier: GPL-2.0 2 3 // Copyright (C) 2024 Google LLC. 4 5 //! Rust misc device sample. 6 7 use core::pin::Pin; 8 9 use kernel::{ 10 c_str, 11 device::Device, 12 fs::File, 13 ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, 14 miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, 15 new_mutex, 16 prelude::*, 17 sync::Mutex, 18 types::ARef, 19 uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, 20 }; 21 22 const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80); 23 const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81); 24 const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82); 25 26 module! { 27 type: RustMiscDeviceModule, 28 name: "rust_misc_device", 29 author: "Lee Jones", 30 description: "Rust misc device sample", 31 license: "GPL", 32 } 33 34 #[pin_data] 35 struct RustMiscDeviceModule { 36 #[pin] 37 _miscdev: MiscDeviceRegistration<RustMiscDevice>, 38 } 39 40 impl kernel::InPlaceModule for RustMiscDeviceModule { 41 fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> { 42 pr_info!("Initialising Rust Misc Device Sample\n"); 43 44 let options = MiscDeviceOptions { 45 name: c_str!("rust-misc-device"), 46 }; 47 48 try_pin_init!(Self { 49 _miscdev <- MiscDeviceRegistration::register(options), 50 }) 51 } 52 } 53 54 struct Inner { 55 value: i32, 56 } 57 58 #[pin_data(PinnedDrop)] 59 struct RustMiscDevice { 60 #[pin] 61 inner: Mutex<Inner>, 62 dev: ARef<Device>, 63 } 64 65 #[vtable] 66 impl MiscDevice for RustMiscDevice { 67 type Ptr = Pin<KBox<Self>>; 68 69 fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> { 70 let dev = ARef::from(misc.device()); 71 72 dev_info!(dev, "Opening Rust Misc Device Sample\n"); 73 74 KBox::try_pin_init( 75 try_pin_init! { 76 RustMiscDevice { 77 inner <- new_mutex!( Inner{ value: 0_i32 } ), 78 dev: dev, 79 } 80 }, 81 GFP_KERNEL, 82 ) 83 } 84 85 fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> { 86 dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n"); 87 88 let size = _IOC_SIZE(cmd); 89 90 match cmd { 91 RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?, 92 RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?, 93 RUST_MISC_DEV_HELLO => me.hello()?, 94 _ => { 95 dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); 96 return Err(ENOTTY); 97 } 98 }; 99 100 Ok(0) 101 } 102 } 103 104 #[pinned_drop] 105 impl PinnedDrop for RustMiscDevice { 106 fn drop(self: Pin<&mut Self>) { 107 dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n"); 108 } 109 } 110 111 impl RustMiscDevice { 112 fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> { 113 let new_value = reader.read::<i32>()?; 114 let mut guard = self.inner.lock(); 115 116 dev_info!( 117 self.dev, 118 "-> Copying data from userspace (value: {})\n", 119 new_value 120 ); 121 122 guard.value = new_value; 123 Ok(0) 124 } 125 126 fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> { 127 let guard = self.inner.lock(); 128 let value = guard.value; 129 130 // Free-up the lock and use our locally cached instance from here 131 drop(guard); 132 133 dev_info!( 134 self.dev, 135 "-> Copying data to userspace (value: {})\n", 136 &value 137 ); 138 139 writer.write::<i32>(&value)?; 140 Ok(0) 141 } 142 143 fn hello(&self) -> Result<isize> { 144 dev_info!(self.dev, "-> Hello from the Rust Misc Device\n"); 145 146 Ok(0) 147 } 148 } 149