1fdb1ac6cSLee Jones // SPDX-License-Identifier: GPL-2.0 2fdb1ac6cSLee Jones 3fdb1ac6cSLee Jones // Copyright (C) 2024 Google LLC. 4fdb1ac6cSLee Jones 5fdb1ac6cSLee Jones //! Rust misc device sample. 6*a24b18aaSAlice Ryhl //! 7*a24b18aaSAlice Ryhl //! Below is an example userspace C program that exercises this sample's functionality. 8*a24b18aaSAlice Ryhl //! 9*a24b18aaSAlice Ryhl //! ```c 10*a24b18aaSAlice Ryhl //! #include <stdio.h> 11*a24b18aaSAlice Ryhl //! #include <stdlib.h> 12*a24b18aaSAlice Ryhl //! #include <errno.h> 13*a24b18aaSAlice Ryhl //! #include <fcntl.h> 14*a24b18aaSAlice Ryhl //! #include <unistd.h> 15*a24b18aaSAlice Ryhl //! #include <sys/ioctl.h> 16*a24b18aaSAlice Ryhl //! 17*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_FAIL _IO('|', 0) 18*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_HELLO _IO('|', 0x80) 19*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int) 20*a24b18aaSAlice Ryhl //! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int) 21*a24b18aaSAlice Ryhl //! 22*a24b18aaSAlice Ryhl //! int main() { 23*a24b18aaSAlice Ryhl //! int value, new_value; 24*a24b18aaSAlice Ryhl //! int fd, ret; 25*a24b18aaSAlice Ryhl //! 26*a24b18aaSAlice Ryhl //! // Open the device file 27*a24b18aaSAlice Ryhl //! printf("Opening /dev/rust-misc-device for reading and writing\n"); 28*a24b18aaSAlice Ryhl //! fd = open("/dev/rust-misc-device", O_RDWR); 29*a24b18aaSAlice Ryhl //! if (fd < 0) { 30*a24b18aaSAlice Ryhl //! perror("open"); 31*a24b18aaSAlice Ryhl //! return errno; 32*a24b18aaSAlice Ryhl //! } 33*a24b18aaSAlice Ryhl //! 34*a24b18aaSAlice Ryhl //! // Make call into driver to say "hello" 35*a24b18aaSAlice Ryhl //! printf("Calling Hello\n"); 36*a24b18aaSAlice Ryhl //! ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL); 37*a24b18aaSAlice Ryhl //! if (ret < 0) { 38*a24b18aaSAlice Ryhl //! perror("ioctl: Failed to call into Hello"); 39*a24b18aaSAlice Ryhl //! close(fd); 40*a24b18aaSAlice Ryhl //! return errno; 41*a24b18aaSAlice Ryhl //! } 42*a24b18aaSAlice Ryhl //! 43*a24b18aaSAlice Ryhl //! // Get initial value 44*a24b18aaSAlice Ryhl //! printf("Fetching initial value\n"); 45*a24b18aaSAlice Ryhl //! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value); 46*a24b18aaSAlice Ryhl //! if (ret < 0) { 47*a24b18aaSAlice Ryhl //! perror("ioctl: Failed to fetch the initial value"); 48*a24b18aaSAlice Ryhl //! close(fd); 49*a24b18aaSAlice Ryhl //! return errno; 50*a24b18aaSAlice Ryhl //! } 51*a24b18aaSAlice Ryhl //! 52*a24b18aaSAlice Ryhl //! value++; 53*a24b18aaSAlice Ryhl //! 54*a24b18aaSAlice Ryhl //! // Set value to something different 55*a24b18aaSAlice Ryhl //! printf("Submitting new value (%d)\n", value); 56*a24b18aaSAlice Ryhl //! ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value); 57*a24b18aaSAlice Ryhl //! if (ret < 0) { 58*a24b18aaSAlice Ryhl //! perror("ioctl: Failed to submit new value"); 59*a24b18aaSAlice Ryhl //! close(fd); 60*a24b18aaSAlice Ryhl //! return errno; 61*a24b18aaSAlice Ryhl //! } 62*a24b18aaSAlice Ryhl //! 63*a24b18aaSAlice Ryhl //! // Ensure new value was applied 64*a24b18aaSAlice Ryhl //! printf("Fetching new value\n"); 65*a24b18aaSAlice Ryhl //! ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value); 66*a24b18aaSAlice Ryhl //! if (ret < 0) { 67*a24b18aaSAlice Ryhl //! perror("ioctl: Failed to fetch the new value"); 68*a24b18aaSAlice Ryhl //! close(fd); 69*a24b18aaSAlice Ryhl //! return errno; 70*a24b18aaSAlice Ryhl //! } 71*a24b18aaSAlice Ryhl //! 72*a24b18aaSAlice Ryhl //! if (value != new_value) { 73*a24b18aaSAlice Ryhl //! printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value); 74*a24b18aaSAlice Ryhl //! close(fd); 75*a24b18aaSAlice Ryhl //! return -1; 76*a24b18aaSAlice Ryhl //! } 77*a24b18aaSAlice Ryhl //! 78*a24b18aaSAlice Ryhl //! // Call the unsuccessful ioctl 79*a24b18aaSAlice Ryhl //! printf("Attempting to call in to an non-existent IOCTL\n"); 80*a24b18aaSAlice Ryhl //! ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL); 81*a24b18aaSAlice Ryhl //! if (ret < 0) { 82*a24b18aaSAlice Ryhl //! perror("ioctl: Succeeded to fail - this was expected"); 83*a24b18aaSAlice Ryhl //! } else { 84*a24b18aaSAlice Ryhl //! printf("ioctl: Failed to fail\n"); 85*a24b18aaSAlice Ryhl //! close(fd); 86*a24b18aaSAlice Ryhl //! return -1; 87*a24b18aaSAlice Ryhl //! } 88*a24b18aaSAlice Ryhl //! 89*a24b18aaSAlice Ryhl //! // Close the device file 90*a24b18aaSAlice Ryhl //! printf("Closing /dev/rust-misc-device\n"); 91*a24b18aaSAlice Ryhl //! close(fd); 92*a24b18aaSAlice Ryhl //! 93*a24b18aaSAlice Ryhl //! printf("Success\n"); 94*a24b18aaSAlice Ryhl //! return 0; 95*a24b18aaSAlice Ryhl //! } 96*a24b18aaSAlice Ryhl //! ``` 97fdb1ac6cSLee Jones 9842523cebSLee Jones use core::pin::Pin; 9942523cebSLee Jones 100fdb1ac6cSLee Jones use kernel::{ 101fdb1ac6cSLee Jones c_str, 102fdb1ac6cSLee Jones device::Device, 103fdb1ac6cSLee Jones fs::File, 10442523cebSLee Jones ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, 105fdb1ac6cSLee Jones miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, 10642523cebSLee Jones new_mutex, 107fdb1ac6cSLee Jones prelude::*, 10842523cebSLee Jones sync::Mutex, 109fdb1ac6cSLee Jones types::ARef, 11042523cebSLee Jones uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, 111fdb1ac6cSLee Jones }; 112fdb1ac6cSLee Jones 113fdb1ac6cSLee Jones const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80); 11442523cebSLee Jones const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81); 11542523cebSLee Jones const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82); 116fdb1ac6cSLee Jones 117fdb1ac6cSLee Jones module! { 118fdb1ac6cSLee Jones type: RustMiscDeviceModule, 119fdb1ac6cSLee Jones name: "rust_misc_device", 12038559da6SGuilherme Giacomo Simoes authors: ["Lee Jones"], 121fdb1ac6cSLee Jones description: "Rust misc device sample", 122fdb1ac6cSLee Jones license: "GPL", 123fdb1ac6cSLee Jones } 124fdb1ac6cSLee Jones 125fdb1ac6cSLee Jones #[pin_data] 126fdb1ac6cSLee Jones struct RustMiscDeviceModule { 127fdb1ac6cSLee Jones #[pin] 128fdb1ac6cSLee Jones _miscdev: MiscDeviceRegistration<RustMiscDevice>, 129fdb1ac6cSLee Jones } 130fdb1ac6cSLee Jones 131fdb1ac6cSLee Jones impl kernel::InPlaceModule for RustMiscDeviceModule { 132fdb1ac6cSLee Jones fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> { 133fdb1ac6cSLee Jones pr_info!("Initialising Rust Misc Device Sample\n"); 134fdb1ac6cSLee Jones 135fdb1ac6cSLee Jones let options = MiscDeviceOptions { 136fdb1ac6cSLee Jones name: c_str!("rust-misc-device"), 137fdb1ac6cSLee Jones }; 138fdb1ac6cSLee Jones 139fdb1ac6cSLee Jones try_pin_init!(Self { 140fdb1ac6cSLee Jones _miscdev <- MiscDeviceRegistration::register(options), 141fdb1ac6cSLee Jones }) 142fdb1ac6cSLee Jones } 143fdb1ac6cSLee Jones } 144fdb1ac6cSLee Jones 14542523cebSLee Jones struct Inner { 14642523cebSLee Jones value: i32, 14742523cebSLee Jones } 14842523cebSLee Jones 14942523cebSLee Jones #[pin_data(PinnedDrop)] 150fdb1ac6cSLee Jones struct RustMiscDevice { 15142523cebSLee Jones #[pin] 15242523cebSLee Jones inner: Mutex<Inner>, 153fdb1ac6cSLee Jones dev: ARef<Device>, 154fdb1ac6cSLee Jones } 155fdb1ac6cSLee Jones 156fdb1ac6cSLee Jones #[vtable] 157fdb1ac6cSLee Jones impl MiscDevice for RustMiscDevice { 15842523cebSLee Jones type Ptr = Pin<KBox<Self>>; 159fdb1ac6cSLee Jones 16042523cebSLee Jones fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> { 161fdb1ac6cSLee Jones let dev = ARef::from(misc.device()); 162fdb1ac6cSLee Jones 163fdb1ac6cSLee Jones dev_info!(dev, "Opening Rust Misc Device Sample\n"); 164fdb1ac6cSLee Jones 16542523cebSLee Jones KBox::try_pin_init( 16642523cebSLee Jones try_pin_init! { 16742523cebSLee Jones RustMiscDevice { 16842523cebSLee Jones inner <- new_mutex!( Inner{ value: 0_i32 } ), 16942523cebSLee Jones dev: dev, 17042523cebSLee Jones } 17142523cebSLee Jones }, 17242523cebSLee Jones GFP_KERNEL, 17342523cebSLee Jones ) 174fdb1ac6cSLee Jones } 175fdb1ac6cSLee Jones 17642523cebSLee Jones fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> { 177fdb1ac6cSLee Jones dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n"); 178fdb1ac6cSLee Jones 17942523cebSLee Jones let size = _IOC_SIZE(cmd); 18042523cebSLee Jones 181fdb1ac6cSLee Jones match cmd { 18242523cebSLee Jones RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?, 18342523cebSLee Jones RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?, 18442523cebSLee Jones RUST_MISC_DEV_HELLO => me.hello()?, 185fdb1ac6cSLee Jones _ => { 186fdb1ac6cSLee Jones dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); 187fdb1ac6cSLee Jones return Err(ENOTTY); 188fdb1ac6cSLee Jones } 18942523cebSLee Jones }; 190fdb1ac6cSLee Jones 191fdb1ac6cSLee Jones Ok(0) 192fdb1ac6cSLee Jones } 193fdb1ac6cSLee Jones } 194fdb1ac6cSLee Jones 19542523cebSLee Jones #[pinned_drop] 19642523cebSLee Jones impl PinnedDrop for RustMiscDevice { 19742523cebSLee Jones fn drop(self: Pin<&mut Self>) { 198fdb1ac6cSLee Jones dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n"); 199fdb1ac6cSLee Jones } 200fdb1ac6cSLee Jones } 20142523cebSLee Jones 20242523cebSLee Jones impl RustMiscDevice { 20342523cebSLee Jones fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> { 20442523cebSLee Jones let new_value = reader.read::<i32>()?; 20542523cebSLee Jones let mut guard = self.inner.lock(); 20642523cebSLee Jones 20742523cebSLee Jones dev_info!( 20842523cebSLee Jones self.dev, 20942523cebSLee Jones "-> Copying data from userspace (value: {})\n", 21042523cebSLee Jones new_value 21142523cebSLee Jones ); 21242523cebSLee Jones 21342523cebSLee Jones guard.value = new_value; 21442523cebSLee Jones Ok(0) 21542523cebSLee Jones } 21642523cebSLee Jones 21742523cebSLee Jones fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> { 21842523cebSLee Jones let guard = self.inner.lock(); 21942523cebSLee Jones let value = guard.value; 22042523cebSLee Jones 22142523cebSLee Jones // Free-up the lock and use our locally cached instance from here 22242523cebSLee Jones drop(guard); 22342523cebSLee Jones 22442523cebSLee Jones dev_info!( 22542523cebSLee Jones self.dev, 22642523cebSLee Jones "-> Copying data to userspace (value: {})\n", 22742523cebSLee Jones &value 22842523cebSLee Jones ); 22942523cebSLee Jones 23042523cebSLee Jones writer.write::<i32>(&value)?; 23142523cebSLee Jones Ok(0) 23242523cebSLee Jones } 23342523cebSLee Jones 23442523cebSLee Jones fn hello(&self) -> Result<isize> { 23542523cebSLee Jones dev_info!(self.dev, "-> Hello from the Rust Misc Device\n"); 23642523cebSLee Jones 23742523cebSLee Jones Ok(0) 23842523cebSLee Jones } 23942523cebSLee Jones } 240