Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/samples/rust/rust_misc_device.rs
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
// Copyright (C) 2024 Google LLC.
4
5
//! Rust misc device sample.
6
//!
7
//! Below is an example userspace C program that exercises this sample's functionality.
8
//!
9
//! ```c
10
//! #include <stdio.h>
11
//! #include <stdlib.h>
12
//! #include <errno.h>
13
//! #include <fcntl.h>
14
//! #include <unistd.h>
15
//! #include <sys/ioctl.h>
16
//!
17
//! #define RUST_MISC_DEV_FAIL _IO('|', 0)
18
//! #define RUST_MISC_DEV_HELLO _IO('|', 0x80)
19
//! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)
20
//! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)
21
//!
22
//! int main() {
23
//! int value, new_value;
24
//! int fd, ret;
25
//!
26
//! // Open the device file
27
//! printf("Opening /dev/rust-misc-device for reading and writing\n");
28
//! fd = open("/dev/rust-misc-device", O_RDWR);
29
//! if (fd < 0) {
30
//! perror("open");
31
//! return errno;
32
//! }
33
//!
34
//! // Make call into driver to say "hello"
35
//! printf("Calling Hello\n");
36
//! ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);
37
//! if (ret < 0) {
38
//! perror("ioctl: Failed to call into Hello");
39
//! close(fd);
40
//! return errno;
41
//! }
42
//!
43
//! // Get initial value
44
//! printf("Fetching initial value\n");
45
//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);
46
//! if (ret < 0) {
47
//! perror("ioctl: Failed to fetch the initial value");
48
//! close(fd);
49
//! return errno;
50
//! }
51
//!
52
//! value++;
53
//!
54
//! // Set value to something different
55
//! printf("Submitting new value (%d)\n", value);
56
//! ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);
57
//! if (ret < 0) {
58
//! perror("ioctl: Failed to submit new value");
59
//! close(fd);
60
//! return errno;
61
//! }
62
//!
63
//! // Ensure new value was applied
64
//! printf("Fetching new value\n");
65
//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);
66
//! if (ret < 0) {
67
//! perror("ioctl: Failed to fetch the new value");
68
//! close(fd);
69
//! return errno;
70
//! }
71
//!
72
//! if (value != new_value) {
73
//! printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);
74
//! close(fd);
75
//! return -1;
76
//! }
77
//!
78
//! // Call the unsuccessful ioctl
79
//! printf("Attempting to call in to an non-existent IOCTL\n");
80
//! ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);
81
//! if (ret < 0) {
82
//! perror("ioctl: Succeeded to fail - this was expected");
83
//! } else {
84
//! printf("ioctl: Failed to fail\n");
85
//! close(fd);
86
//! return -1;
87
//! }
88
//!
89
//! // Close the device file
90
//! printf("Closing /dev/rust-misc-device\n");
91
//! close(fd);
92
//!
93
//! printf("Success\n");
94
//! return 0;
95
//! }
96
//! ```
97
98
use core::pin::Pin;
99
100
use kernel::{
101
c_str,
102
device::Device,
103
fs::File,
104
ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
105
miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
106
new_mutex,
107
prelude::*,
108
sync::Mutex,
109
types::ARef,
110
uaccess::{UserSlice, UserSliceReader, UserSliceWriter},
111
};
112
113
const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);
114
const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);
115
const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);
116
117
module! {
118
type: RustMiscDeviceModule,
119
name: "rust_misc_device",
120
authors: ["Lee Jones"],
121
description: "Rust misc device sample",
122
license: "GPL",
123
}
124
125
#[pin_data]
126
struct RustMiscDeviceModule {
127
#[pin]
128
_miscdev: MiscDeviceRegistration<RustMiscDevice>,
129
}
130
131
impl kernel::InPlaceModule for RustMiscDeviceModule {
132
fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
133
pr_info!("Initialising Rust Misc Device Sample\n");
134
135
let options = MiscDeviceOptions {
136
name: c_str!("rust-misc-device"),
137
};
138
139
try_pin_init!(Self {
140
_miscdev <- MiscDeviceRegistration::register(options),
141
})
142
}
143
}
144
145
struct Inner {
146
value: i32,
147
}
148
149
#[pin_data(PinnedDrop)]
150
struct RustMiscDevice {
151
#[pin]
152
inner: Mutex<Inner>,
153
dev: ARef<Device>,
154
}
155
156
#[vtable]
157
impl MiscDevice for RustMiscDevice {
158
type Ptr = Pin<KBox<Self>>;
159
160
fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
161
let dev = ARef::from(misc.device());
162
163
dev_info!(dev, "Opening Rust Misc Device Sample\n");
164
165
KBox::try_pin_init(
166
try_pin_init! {
167
RustMiscDevice {
168
inner <- new_mutex!( Inner{ value: 0_i32 } ),
169
dev: dev,
170
}
171
},
172
GFP_KERNEL,
173
)
174
}
175
176
fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
177
dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
178
179
// Treat the ioctl argument as a user pointer.
180
let arg = UserPtr::from_addr(arg);
181
let size = _IOC_SIZE(cmd);
182
183
match cmd {
184
RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,
185
RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,
186
RUST_MISC_DEV_HELLO => me.hello()?,
187
_ => {
188
dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
189
return Err(ENOTTY);
190
}
191
};
192
193
Ok(0)
194
}
195
}
196
197
#[pinned_drop]
198
impl PinnedDrop for RustMiscDevice {
199
fn drop(self: Pin<&mut Self>) {
200
dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
201
}
202
}
203
204
impl RustMiscDevice {
205
fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {
206
let new_value = reader.read::<i32>()?;
207
let mut guard = self.inner.lock();
208
209
dev_info!(
210
self.dev,
211
"-> Copying data from userspace (value: {})\n",
212
new_value
213
);
214
215
guard.value = new_value;
216
Ok(0)
217
}
218
219
fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {
220
let guard = self.inner.lock();
221
let value = guard.value;
222
223
// Free-up the lock and use our locally cached instance from here
224
drop(guard);
225
226
dev_info!(
227
self.dev,
228
"-> Copying data to userspace (value: {})\n",
229
&value
230
);
231
232
writer.write::<i32>(&value)?;
233
Ok(0)
234
}
235
236
fn hello(&self) -> Result<isize> {
237
dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
238
239
Ok(0)
240
}
241
}
242
243