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