// 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,103ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},104miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},105new_mutex,106prelude::*,107sync::Mutex,108types::ARef,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,146}147148#[pin_data(PinnedDrop)]149struct RustMiscDevice {150#[pin]151inner: Mutex<Inner>,152dev: ARef<Device>,153}154155#[vtable]156impl MiscDevice for RustMiscDevice {157type Ptr = Pin<KBox<Self>>;158159fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {160let dev = ARef::from(misc.device());161162dev_info!(dev, "Opening Rust Misc Device Sample\n");163164KBox::try_pin_init(165try_pin_init! {166RustMiscDevice {167inner <- new_mutex!( Inner{ value: 0_i32 } ),168dev: dev,169}170},171GFP_KERNEL,172)173}174175fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {176dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");177178// Treat the ioctl argument as a user pointer.179let arg = UserPtr::from_addr(arg);180let size = _IOC_SIZE(cmd);181182match cmd {183RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,184RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,185RUST_MISC_DEV_HELLO => me.hello()?,186_ => {187dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);188return Err(ENOTTY);189}190};191192Ok(0)193}194}195196#[pinned_drop]197impl PinnedDrop for RustMiscDevice {198fn drop(self: Pin<&mut Self>) {199dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");200}201}202203impl RustMiscDevice {204fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {205let new_value = reader.read::<i32>()?;206let mut guard = self.inner.lock();207208dev_info!(209self.dev,210"-> Copying data from userspace (value: {})\n",211new_value212);213214guard.value = new_value;215Ok(0)216}217218fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {219let guard = self.inner.lock();220let value = guard.value;221222// Free-up the lock and use our locally cached instance from here223drop(guard);224225dev_info!(226self.dev,227"-> Copying data to userspace (value: {})\n",228&value229);230231writer.write::<i32>(&value)?;232Ok(0)233}234235fn hello(&self) -> Result<isize> {236dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");237238Ok(0)239}240}241242243