Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/samples/rust/rust_misc_device.rs
29508 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, Kiocb},
104
ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
105
iov::{IovIterDest, IovIterSource},
106
miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
107
new_mutex,
108
prelude::*,
109
sync::{aref::ARef, Mutex},
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
buffer: KVVec<u8>,
148
}
149
150
#[pin_data(PinnedDrop)]
151
struct RustMiscDevice {
152
#[pin]
153
inner: Mutex<Inner>,
154
dev: ARef<Device>,
155
}
156
157
#[vtable]
158
impl MiscDevice for RustMiscDevice {
159
type Ptr = Pin<KBox<Self>>;
160
161
fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
162
let dev = ARef::from(misc.device());
163
164
dev_info!(dev, "Opening Rust Misc Device Sample\n");
165
166
KBox::try_pin_init(
167
try_pin_init! {
168
RustMiscDevice {
169
inner <- new_mutex!(Inner {
170
value: 0_i32,
171
buffer: KVVec::new(),
172
}),
173
dev: dev,
174
}
175
},
176
GFP_KERNEL,
177
)
178
}
179
180
fn read_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterDest<'_>) -> Result<usize> {
181
let me = kiocb.file();
182
dev_info!(me.dev, "Reading from Rust Misc Device Sample\n");
183
184
let inner = me.inner.lock();
185
// Read the buffer contents, taking the file position into account.
186
let read = iov.simple_read_from_buffer(kiocb.ki_pos_mut(), &inner.buffer)?;
187
188
Ok(read)
189
}
190
191
fn write_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterSource<'_>) -> Result<usize> {
192
let me = kiocb.file();
193
dev_info!(me.dev, "Writing to Rust Misc Device Sample\n");
194
195
let mut inner = me.inner.lock();
196
197
// Replace buffer contents.
198
inner.buffer.clear();
199
let len = iov.copy_from_iter_vec(&mut inner.buffer, GFP_KERNEL)?;
200
201
// Set position to zero so that future `read` calls will see the new contents.
202
*kiocb.ki_pos_mut() = 0;
203
204
Ok(len)
205
}
206
207
fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
208
dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
209
210
// Treat the ioctl argument as a user pointer.
211
let arg = UserPtr::from_addr(arg);
212
let size = _IOC_SIZE(cmd);
213
214
match cmd {
215
RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,
216
RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,
217
RUST_MISC_DEV_HELLO => me.hello()?,
218
_ => {
219
dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
220
return Err(ENOTTY);
221
}
222
};
223
224
Ok(0)
225
}
226
}
227
228
#[pinned_drop]
229
impl PinnedDrop for RustMiscDevice {
230
fn drop(self: Pin<&mut Self>) {
231
dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
232
}
233
}
234
235
impl RustMiscDevice {
236
fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {
237
let new_value = reader.read::<i32>()?;
238
let mut guard = self.inner.lock();
239
240
dev_info!(
241
self.dev,
242
"-> Copying data from userspace (value: {})\n",
243
new_value
244
);
245
246
guard.value = new_value;
247
Ok(0)
248
}
249
250
fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {
251
let guard = self.inner.lock();
252
let value = guard.value;
253
254
// Free-up the lock and use our locally cached instance from here
255
drop(guard);
256
257
dev_info!(
258
self.dev,
259
"-> Copying data to userspace (value: {})\n",
260
&value
261
);
262
263
writer.write::<i32>(&value)?;
264
Ok(0)
265
}
266
267
fn hello(&self) -> Result<isize> {
268
dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
269
270
Ok(0)
271
}
272
}
273
274