// SPDX-License-Identifier: GPL-2.012// Copyright (C) 2024 Google LLC.34//! Rust misc device sample.5//!6//! Below is an example userspace C program that exercises this sample's functionality.7//!8//! ```c9//! #include <stdio.h>10//! #include <stdlib.h>11//! #include <errno.h>12//! #include <fcntl.h>13//! #include <unistd.h>14//! #include <sys/ioctl.h>15//!16//! #define RUST_MISC_DEV_FAIL _IO('|', 0)17//! #define RUST_MISC_DEV_HELLO _IO('|', 0x80)18//! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)19//! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)20//!21//! int main() {22//! int value, new_value;23//! int fd, ret;24//!25//! // Open the device file26//! printf("Opening /dev/rust-misc-device for reading and writing\n");27//! fd = open("/dev/rust-misc-device", O_RDWR);28//! if (fd < 0) {29//! perror("open");30//! return errno;31//! }32//!33//! // Make call into driver to say "hello"34//! printf("Calling Hello\n");35//! ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);36//! if (ret < 0) {37//! perror("ioctl: Failed to call into Hello");38//! close(fd);39//! return errno;40//! }41//!42//! // Get initial value43//! printf("Fetching initial value\n");44//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);45//! if (ret < 0) {46//! perror("ioctl: Failed to fetch the initial value");47//! close(fd);48//! return errno;49//! }50//!51//! value++;52//!53//! // Set value to something different54//! printf("Submitting new value (%d)\n", value);55//! ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);56//! if (ret < 0) {57//! perror("ioctl: Failed to submit new value");58//! close(fd);59//! return errno;60//! }61//!62//! // Ensure new value was applied63//! printf("Fetching new value\n");64//! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);65//! if (ret < 0) {66//! perror("ioctl: Failed to fetch the new value");67//! close(fd);68//! return errno;69//! }70//!71//! if (value != new_value) {72//! printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);73//! close(fd);74//! return -1;75//! }76//!77//! // Call the unsuccessful ioctl78//! printf("Attempting to call in to an non-existent IOCTL\n");79//! ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);80//! if (ret < 0) {81//! perror("ioctl: Succeeded to fail - this was expected");82//! } else {83//! printf("ioctl: Failed to fail\n");84//! close(fd);85//! return -1;86//! }87//!88//! // Close the device file89//! printf("Closing /dev/rust-misc-device\n");90//! close(fd);91//!92//! printf("Success\n");93//! return 0;94//! }95//! ```9697use core::pin::Pin;9899use kernel::{100c_str,101device::Device,102fs::{File, Kiocb},103ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},104iov::{IovIterDest, IovIterSource},105miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},106new_mutex,107prelude::*,108sync::{aref::ARef, Mutex},109uaccess::{UserSlice, UserSliceReader, UserSliceWriter},110};111112const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);113const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);114const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);115116module! {117type: RustMiscDeviceModule,118name: "rust_misc_device",119authors: ["Lee Jones"],120description: "Rust misc device sample",121license: "GPL",122}123124#[pin_data]125struct RustMiscDeviceModule {126#[pin]127_miscdev: MiscDeviceRegistration<RustMiscDevice>,128}129130impl kernel::InPlaceModule for RustMiscDeviceModule {131fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {132pr_info!("Initialising Rust Misc Device Sample\n");133134let options = MiscDeviceOptions {135name: c_str!("rust-misc-device"),136};137138try_pin_init!(Self {139_miscdev <- MiscDeviceRegistration::register(options),140})141}142}143144struct Inner {145value: i32,146buffer: KVVec<u8>,147}148149#[pin_data(PinnedDrop)]150struct RustMiscDevice {151#[pin]152inner: Mutex<Inner>,153dev: ARef<Device>,154}155156#[vtable]157impl MiscDevice for RustMiscDevice {158type Ptr = Pin<KBox<Self>>;159160fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {161let dev = ARef::from(misc.device());162163dev_info!(dev, "Opening Rust Misc Device Sample\n");164165KBox::try_pin_init(166try_pin_init! {167RustMiscDevice {168inner <- new_mutex!(Inner {169value: 0_i32,170buffer: KVVec::new(),171}),172dev: dev,173}174},175GFP_KERNEL,176)177}178179fn read_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterDest<'_>) -> Result<usize> {180let me = kiocb.file();181dev_info!(me.dev, "Reading from Rust Misc Device Sample\n");182183let inner = me.inner.lock();184// Read the buffer contents, taking the file position into account.185let read = iov.simple_read_from_buffer(kiocb.ki_pos_mut(), &inner.buffer)?;186187Ok(read)188}189190fn write_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterSource<'_>) -> Result<usize> {191let me = kiocb.file();192dev_info!(me.dev, "Writing to Rust Misc Device Sample\n");193194let mut inner = me.inner.lock();195196// Replace buffer contents.197inner.buffer.clear();198let len = iov.copy_from_iter_vec(&mut inner.buffer, GFP_KERNEL)?;199200// Set position to zero so that future `read` calls will see the new contents.201*kiocb.ki_pos_mut() = 0;202203Ok(len)204}205206fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {207dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");208209// Treat the ioctl argument as a user pointer.210let arg = UserPtr::from_addr(arg);211let size = _IOC_SIZE(cmd);212213match cmd {214RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,215RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,216RUST_MISC_DEV_HELLO => me.hello()?,217_ => {218dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);219return Err(ENOTTY);220}221};222223Ok(0)224}225}226227#[pinned_drop]228impl PinnedDrop for RustMiscDevice {229fn drop(self: Pin<&mut Self>) {230dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");231}232}233234impl RustMiscDevice {235fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {236let new_value = reader.read::<i32>()?;237let mut guard = self.inner.lock();238239dev_info!(240self.dev,241"-> Copying data from userspace (value: {})\n",242new_value243);244245guard.value = new_value;246Ok(0)247}248249fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {250let guard = self.inner.lock();251let value = guard.value;252253// Free-up the lock and use our locally cached instance from here254drop(guard);255256dev_info!(257self.dev,258"-> Copying data to userspace (value: {})\n",259&value260);261262writer.write::<i32>(&value)?;263Ok(0)264}265266fn hello(&self) -> Result<isize> {267dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");268269Ok(0)270}271}272273274