xref: /linux/samples/rust/rust_misc_device.rs (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1fdb1ac6cSLee Jones // SPDX-License-Identifier: GPL-2.0
2fdb1ac6cSLee Jones 
3fdb1ac6cSLee Jones // Copyright (C) 2024 Google LLC.
4fdb1ac6cSLee Jones 
5fdb1ac6cSLee Jones //! Rust misc device sample.
6*a24b18aaSAlice Ryhl //!
7*a24b18aaSAlice Ryhl //! Below is an example userspace C program that exercises this sample's functionality.
8*a24b18aaSAlice Ryhl //!
9*a24b18aaSAlice Ryhl //! ```c
10*a24b18aaSAlice Ryhl //! #include <stdio.h>
11*a24b18aaSAlice Ryhl //! #include <stdlib.h>
12*a24b18aaSAlice Ryhl //! #include <errno.h>
13*a24b18aaSAlice Ryhl //! #include <fcntl.h>
14*a24b18aaSAlice Ryhl //! #include <unistd.h>
15*a24b18aaSAlice Ryhl //! #include <sys/ioctl.h>
16*a24b18aaSAlice Ryhl //!
17*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_FAIL _IO('|', 0)
18*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_HELLO _IO('|', 0x80)
19*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)
20*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)
21*a24b18aaSAlice Ryhl //!
22*a24b18aaSAlice Ryhl //! int main() {
23*a24b18aaSAlice Ryhl //!   int value, new_value;
24*a24b18aaSAlice Ryhl //!   int fd, ret;
25*a24b18aaSAlice Ryhl //!
26*a24b18aaSAlice Ryhl //!   // Open the device file
27*a24b18aaSAlice Ryhl //!   printf("Opening /dev/rust-misc-device for reading and writing\n");
28*a24b18aaSAlice Ryhl //!   fd = open("/dev/rust-misc-device", O_RDWR);
29*a24b18aaSAlice Ryhl //!   if (fd < 0) {
30*a24b18aaSAlice Ryhl //!     perror("open");
31*a24b18aaSAlice Ryhl //!     return errno;
32*a24b18aaSAlice Ryhl //!   }
33*a24b18aaSAlice Ryhl //!
34*a24b18aaSAlice Ryhl //!   // Make call into driver to say "hello"
35*a24b18aaSAlice Ryhl //!   printf("Calling Hello\n");
36*a24b18aaSAlice Ryhl //!   ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);
37*a24b18aaSAlice Ryhl //!   if (ret < 0) {
38*a24b18aaSAlice Ryhl //!     perror("ioctl: Failed to call into Hello");
39*a24b18aaSAlice Ryhl //!     close(fd);
40*a24b18aaSAlice Ryhl //!     return errno;
41*a24b18aaSAlice Ryhl //!   }
42*a24b18aaSAlice Ryhl //!
43*a24b18aaSAlice Ryhl //!   // Get initial value
44*a24b18aaSAlice Ryhl //!   printf("Fetching initial value\n");
45*a24b18aaSAlice Ryhl //!   ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);
46*a24b18aaSAlice Ryhl //!   if (ret < 0) {
47*a24b18aaSAlice Ryhl //!     perror("ioctl: Failed to fetch the initial value");
48*a24b18aaSAlice Ryhl //!     close(fd);
49*a24b18aaSAlice Ryhl //!     return errno;
50*a24b18aaSAlice Ryhl //!   }
51*a24b18aaSAlice Ryhl //!
52*a24b18aaSAlice Ryhl //!   value++;
53*a24b18aaSAlice Ryhl //!
54*a24b18aaSAlice Ryhl //!   // Set value to something different
55*a24b18aaSAlice Ryhl //!   printf("Submitting new value (%d)\n", value);
56*a24b18aaSAlice Ryhl //!   ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);
57*a24b18aaSAlice Ryhl //!   if (ret < 0) {
58*a24b18aaSAlice Ryhl //!     perror("ioctl: Failed to submit new value");
59*a24b18aaSAlice Ryhl //!     close(fd);
60*a24b18aaSAlice Ryhl //!     return errno;
61*a24b18aaSAlice Ryhl //!   }
62*a24b18aaSAlice Ryhl //!
63*a24b18aaSAlice Ryhl //!   // Ensure new value was applied
64*a24b18aaSAlice Ryhl //!   printf("Fetching new value\n");
65*a24b18aaSAlice Ryhl //!   ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);
66*a24b18aaSAlice Ryhl //!   if (ret < 0) {
67*a24b18aaSAlice Ryhl //!     perror("ioctl: Failed to fetch the new value");
68*a24b18aaSAlice Ryhl //!     close(fd);
69*a24b18aaSAlice Ryhl //!     return errno;
70*a24b18aaSAlice Ryhl //!   }
71*a24b18aaSAlice Ryhl //!
72*a24b18aaSAlice Ryhl //!   if (value != new_value) {
73*a24b18aaSAlice Ryhl //!     printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);
74*a24b18aaSAlice Ryhl //!     close(fd);
75*a24b18aaSAlice Ryhl //!     return -1;
76*a24b18aaSAlice Ryhl //!   }
77*a24b18aaSAlice Ryhl //!
78*a24b18aaSAlice Ryhl //!   // Call the unsuccessful ioctl
79*a24b18aaSAlice Ryhl //!   printf("Attempting to call in to an non-existent IOCTL\n");
80*a24b18aaSAlice Ryhl //!   ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);
81*a24b18aaSAlice Ryhl //!   if (ret < 0) {
82*a24b18aaSAlice Ryhl //!     perror("ioctl: Succeeded to fail - this was expected");
83*a24b18aaSAlice Ryhl //!   } else {
84*a24b18aaSAlice Ryhl //!     printf("ioctl: Failed to fail\n");
85*a24b18aaSAlice Ryhl //!     close(fd);
86*a24b18aaSAlice Ryhl //!     return -1;
87*a24b18aaSAlice Ryhl //!   }
88*a24b18aaSAlice Ryhl //!
89*a24b18aaSAlice Ryhl //!   // Close the device file
90*a24b18aaSAlice Ryhl //!   printf("Closing /dev/rust-misc-device\n");
91*a24b18aaSAlice Ryhl //!   close(fd);
92*a24b18aaSAlice Ryhl //!
93*a24b18aaSAlice Ryhl //!   printf("Success\n");
94*a24b18aaSAlice Ryhl //!   return 0;
95*a24b18aaSAlice Ryhl //! }
96*a24b18aaSAlice Ryhl //! ```
97fdb1ac6cSLee Jones 
9842523cebSLee Jones use core::pin::Pin;
9942523cebSLee Jones 
100fdb1ac6cSLee Jones use kernel::{
101fdb1ac6cSLee Jones     c_str,
102fdb1ac6cSLee Jones     device::Device,
103fdb1ac6cSLee Jones     fs::File,
10442523cebSLee Jones     ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
105fdb1ac6cSLee Jones     miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
10642523cebSLee Jones     new_mutex,
107fdb1ac6cSLee Jones     prelude::*,
10842523cebSLee Jones     sync::Mutex,
109fdb1ac6cSLee Jones     types::ARef,
11042523cebSLee Jones     uaccess::{UserSlice, UserSliceReader, UserSliceWriter},
111fdb1ac6cSLee Jones };
112fdb1ac6cSLee Jones 
113fdb1ac6cSLee Jones const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);
11442523cebSLee Jones const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);
11542523cebSLee Jones const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);
116fdb1ac6cSLee Jones 
117fdb1ac6cSLee Jones module! {
118fdb1ac6cSLee Jones     type: RustMiscDeviceModule,
119fdb1ac6cSLee Jones     name: "rust_misc_device",
12038559da6SGuilherme Giacomo Simoes     authors: ["Lee Jones"],
121fdb1ac6cSLee Jones     description: "Rust misc device sample",
122fdb1ac6cSLee Jones     license: "GPL",
123fdb1ac6cSLee Jones }
124fdb1ac6cSLee Jones 
125fdb1ac6cSLee Jones #[pin_data]
126fdb1ac6cSLee Jones struct RustMiscDeviceModule {
127fdb1ac6cSLee Jones     #[pin]
128fdb1ac6cSLee Jones     _miscdev: MiscDeviceRegistration<RustMiscDevice>,
129fdb1ac6cSLee Jones }
130fdb1ac6cSLee Jones 
131fdb1ac6cSLee Jones impl kernel::InPlaceModule for RustMiscDeviceModule {
132fdb1ac6cSLee Jones     fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
133fdb1ac6cSLee Jones         pr_info!("Initialising Rust Misc Device Sample\n");
134fdb1ac6cSLee Jones 
135fdb1ac6cSLee Jones         let options = MiscDeviceOptions {
136fdb1ac6cSLee Jones             name: c_str!("rust-misc-device"),
137fdb1ac6cSLee Jones         };
138fdb1ac6cSLee Jones 
139fdb1ac6cSLee Jones         try_pin_init!(Self {
140fdb1ac6cSLee Jones             _miscdev <- MiscDeviceRegistration::register(options),
141fdb1ac6cSLee Jones         })
142fdb1ac6cSLee Jones     }
143fdb1ac6cSLee Jones }
144fdb1ac6cSLee Jones 
14542523cebSLee Jones struct Inner {
14642523cebSLee Jones     value: i32,
14742523cebSLee Jones }
14842523cebSLee Jones 
14942523cebSLee Jones #[pin_data(PinnedDrop)]
150fdb1ac6cSLee Jones struct RustMiscDevice {
15142523cebSLee Jones     #[pin]
15242523cebSLee Jones     inner: Mutex<Inner>,
153fdb1ac6cSLee Jones     dev: ARef<Device>,
154fdb1ac6cSLee Jones }
155fdb1ac6cSLee Jones 
156fdb1ac6cSLee Jones #[vtable]
157fdb1ac6cSLee Jones impl MiscDevice for RustMiscDevice {
15842523cebSLee Jones     type Ptr = Pin<KBox<Self>>;
159fdb1ac6cSLee Jones 
16042523cebSLee Jones     fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
161fdb1ac6cSLee Jones         let dev = ARef::from(misc.device());
162fdb1ac6cSLee Jones 
163fdb1ac6cSLee Jones         dev_info!(dev, "Opening Rust Misc Device Sample\n");
164fdb1ac6cSLee Jones 
16542523cebSLee Jones         KBox::try_pin_init(
16642523cebSLee Jones             try_pin_init! {
16742523cebSLee Jones                 RustMiscDevice {
16842523cebSLee Jones                     inner <- new_mutex!( Inner{ value: 0_i32 } ),
16942523cebSLee Jones                     dev: dev,
17042523cebSLee Jones                 }
17142523cebSLee Jones             },
17242523cebSLee Jones             GFP_KERNEL,
17342523cebSLee Jones         )
174fdb1ac6cSLee Jones     }
175fdb1ac6cSLee Jones 
17642523cebSLee Jones     fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
177fdb1ac6cSLee Jones         dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
178fdb1ac6cSLee Jones 
17942523cebSLee Jones         let size = _IOC_SIZE(cmd);
18042523cebSLee Jones 
181fdb1ac6cSLee Jones         match cmd {
18242523cebSLee Jones             RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,
18342523cebSLee Jones             RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,
18442523cebSLee Jones             RUST_MISC_DEV_HELLO => me.hello()?,
185fdb1ac6cSLee Jones             _ => {
186fdb1ac6cSLee Jones                 dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
187fdb1ac6cSLee Jones                 return Err(ENOTTY);
188fdb1ac6cSLee Jones             }
18942523cebSLee Jones         };
190fdb1ac6cSLee Jones 
191fdb1ac6cSLee Jones         Ok(0)
192fdb1ac6cSLee Jones     }
193fdb1ac6cSLee Jones }
194fdb1ac6cSLee Jones 
19542523cebSLee Jones #[pinned_drop]
19642523cebSLee Jones impl PinnedDrop for RustMiscDevice {
19742523cebSLee Jones     fn drop(self: Pin<&mut Self>) {
198fdb1ac6cSLee Jones         dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
199fdb1ac6cSLee Jones     }
200fdb1ac6cSLee Jones }
20142523cebSLee Jones 
20242523cebSLee Jones impl RustMiscDevice {
20342523cebSLee Jones     fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {
20442523cebSLee Jones         let new_value = reader.read::<i32>()?;
20542523cebSLee Jones         let mut guard = self.inner.lock();
20642523cebSLee Jones 
20742523cebSLee Jones         dev_info!(
20842523cebSLee Jones             self.dev,
20942523cebSLee Jones             "-> Copying data from userspace (value: {})\n",
21042523cebSLee Jones             new_value
21142523cebSLee Jones         );
21242523cebSLee Jones 
21342523cebSLee Jones         guard.value = new_value;
21442523cebSLee Jones         Ok(0)
21542523cebSLee Jones     }
21642523cebSLee Jones 
21742523cebSLee Jones     fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {
21842523cebSLee Jones         let guard = self.inner.lock();
21942523cebSLee Jones         let value = guard.value;
22042523cebSLee Jones 
22142523cebSLee Jones         // Free-up the lock and use our locally cached instance from here
22242523cebSLee Jones         drop(guard);
22342523cebSLee Jones 
22442523cebSLee Jones         dev_info!(
22542523cebSLee Jones             self.dev,
22642523cebSLee Jones             "-> Copying data to userspace (value: {})\n",
22742523cebSLee Jones             &value
22842523cebSLee Jones         );
22942523cebSLee Jones 
23042523cebSLee Jones         writer.write::<i32>(&value)?;
23142523cebSLee Jones         Ok(0)
23242523cebSLee Jones     }
23342523cebSLee Jones 
23442523cebSLee Jones     fn hello(&self) -> Result<isize> {
23542523cebSLee Jones         dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
23642523cebSLee Jones 
23742523cebSLee Jones         Ok(0)
23842523cebSLee Jones     }
23942523cebSLee Jones }
240