Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024 Google LLC.
//! Rust misc device sample.//!//! Below is an example userspace C program that exercises this sample's functionality.//!//! ```c//! #include <stdio.h>//! #include <stdlib.h>//! #include <errno.h>//! #include <fcntl.h>//! #include <unistd.h>//! #include <sys/ioctl.h>//!//! #define RUST_MISC_DEV_FAIL _IO('|', 0)//! #define RUST_MISC_DEV_HELLO _IO('|', 0x80)//! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)//! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)//!//! int main() {//! int value, new_value;//! int fd, ret;//!//! // Open the device file//! printf("Opening /dev/rust-misc-device for reading and writing\n");//! fd = open("/dev/rust-misc-device", O_RDWR);//! if (fd < 0) {//! perror("open");//! return errno;//! }//!//! // Make call into driver to say "hello"//! printf("Calling Hello\n");//! ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);//! if (ret < 0) {//! perror("ioctl: Failed to call into Hello");//! close(fd);//! return errno;//! }//!//! // Get initial value//! printf("Fetching initial value\n");//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);//! if (ret < 0) {//! perror("ioctl: Failed to fetch the initial value");//! close(fd);//! return errno;//! }//!//! value++;//!//! // Set value to something different//! printf("Submitting new value (%d)\n", value);//! ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);//! if (ret < 0) {//! perror("ioctl: Failed to submit new value");//! close(fd);//! return errno;//! }//!//! // Ensure new value was applied//! printf("Fetching new value\n");//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);//! if (ret < 0) {//! perror("ioctl: Failed to fetch the new value");//! close(fd);//! return errno;//! }//!//! if (value != new_value) {//! printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);//! close(fd);//! return -1;//! }//!//! // Call the unsuccessful ioctl//! printf("Attempting to call in to an non-existent IOCTL\n");//! ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);//! if (ret < 0) {//! perror("ioctl: Succeeded to fail - this was expected");//! } else {//! printf("ioctl: Failed to fail\n");//! close(fd);//! return -1;//! }//!//! // Close the device file//! printf("Closing /dev/rust-misc-device\n");//! close(fd);//!//! printf("Success\n");//! return 0;//! }//! ```
use kernel::{ device::Device, fs::{ File, Kiocb, // }, ioctl::{ _IO, _IOC_SIZE, _IOR, _IOW, // }, iov::{ IovIterDest, IovIterSource, // }, miscdevice::{ MiscDevice, MiscDeviceOptions, MiscDeviceRegistration, // }, new_mutex, prelude::*, sync::{ aref::ARef, Mutex, // }, uaccess::{ UserSlice, UserSliceReader, UserSliceWriter, // },};
const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);
module! { type: RustMiscDeviceModule, name: "rust_misc_device", authors: ["Lee Jones"], description: "Rust misc device sample", license: "GPL",}
#[pin_data]struct RustMiscDeviceModule { #[pin] _miscdev: MiscDeviceRegistration<RustMiscDevice>,}
impl kernel::InPlaceModule for RustMiscDeviceModule { fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> { pr_info!("Initialising Rust Misc Device Sample\n");
let options = MiscDeviceOptions { name: c"rust-misc-device", };
try_pin_init!(Self { // SAFETY: `_miscdev` is stored in `RustMiscDeviceModule` and dropped when the module // is unloaded; `mem::forget()` is never called on it. _miscdev <- unsafe { MiscDeviceRegistration::register(options, try_pin_init!(RustMiscDevice {})) }, }) }}
struct Inner { value: i32, buffer: KVVec<u8>,}
/// Data shared by all open files for the same instance of this miscdevice.#[pin_data]struct RustMiscDevice {}
/// Data that is stored per file for this miscdevice.#[pin_data(PinnedDrop)]struct RustMiscDeviceFile { #[pin] inner: Mutex<Inner>, dev: ARef<Device>,}
#[vtable]impl MiscDevice for RustMiscDevice { type File = Pin<KBox<RustMiscDeviceFile>>;
fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Self::File> { let dev = ARef::from(misc.device());
dev_info!(dev, "Opening Rust Misc Device Sample\n");
KBox::try_pin_init( try_pin_init! { RustMiscDeviceFile { inner <- new_mutex!(Inner { value: 0_i32, buffer: KVVec::new(), }), dev: dev, } }, GFP_KERNEL, ) }
fn read_iter(mut kiocb: Kiocb<'_, Self::File>, iov: &mut IovIterDest<'_>) -> Result<usize> { let me = kiocb.file(); dev_info!(me.dev, "Reading from Rust Misc Device Sample\n");
let inner = me.inner.lock(); // Read the buffer contents, taking the file position into account. let read = iov.simple_read_from_buffer(kiocb.ki_pos_mut(), &inner.buffer)?;
Ok(read) }
fn write_iter(mut kiocb: Kiocb<'_, Self::File>, iov: &mut IovIterSource<'_>) -> Result<usize> { let me = kiocb.file(); dev_info!(me.dev, "Writing to Rust Misc Device Sample\n");
let mut inner = me.inner.lock();
// Replace buffer contents. inner.buffer.clear(); let len = iov.copy_from_iter_vec(&mut inner.buffer, GFP_KERNEL)?;
// Set position to zero so that future `read` calls will see the new contents. *kiocb.ki_pos_mut() = 0;
Ok(len) }
fn ioctl(me: Pin<&RustMiscDeviceFile>, _file: &File, cmd: u32, arg: usize) -> Result<isize> { dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
// Treat the ioctl argument as a user pointer. let arg = UserPtr::from_addr(arg); let size = _IOC_SIZE(cmd);
match cmd { RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?, RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?, RUST_MISC_DEV_HELLO => me.hello()?, _ => { dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); return Err(ENOTTY); } };
Ok(0) }}
#[pinned_drop]impl PinnedDrop for RustMiscDeviceFile { fn drop(self: Pin<&mut Self>) { dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n"); }}
impl RustMiscDeviceFile { fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> { let new_value = reader.read::<i32>()?; let mut guard = self.inner.lock();
dev_info!( self.dev, "-> Copying data from userspace (value: {})\n", new_value );
guard.value = new_value; Ok(0) }
fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> { let guard = self.inner.lock(); let value = guard.value;
// Free-up the lock and use our locally cached instance from here drop(guard);
dev_info!( self.dev, "-> Copying data to userspace (value: {})\n", &value );
writer.write::<i32>(&value)?; Ok(0) }
fn hello(&self) -> Result<isize> { dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
Ok(0) }}