1f893691eSAlice Ryhl // SPDX-License-Identifier: GPL-2.0 2f893691eSAlice Ryhl 3f893691eSAlice Ryhl // Copyright (C) 2024 Google LLC. 4f893691eSAlice Ryhl 5f893691eSAlice Ryhl //! Miscdevice support. 6f893691eSAlice Ryhl //! 7f893691eSAlice Ryhl //! C headers: [`include/linux/miscdevice.h`](srctree/include/linux/miscdevice.h). 8f893691eSAlice Ryhl //! 9f893691eSAlice Ryhl //! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html> 10f893691eSAlice Ryhl 11f893691eSAlice Ryhl use crate::{ 12f893691eSAlice Ryhl bindings, 13284ae0beSLee Jones device::Device, 14f893691eSAlice Ryhl error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, 1527c7518eSMiguel Ojeda ffi::{c_int, c_long, c_uint, c_ulong}, 160d8a7c7bSAlice Ryhl fs::File, 17f893691eSAlice Ryhl prelude::*, 185bcc8bfeSAlice Ryhl seq_file::SeqFile, 19f893691eSAlice Ryhl str::CStr, 20f893691eSAlice Ryhl types::{ForeignOwnable, Opaque}, 21f893691eSAlice Ryhl }; 2227c7518eSMiguel Ojeda use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; 23f893691eSAlice Ryhl 24f893691eSAlice Ryhl /// Options for creating a misc device. 25f893691eSAlice Ryhl #[derive(Copy, Clone)] 26f893691eSAlice Ryhl pub struct MiscDeviceOptions { 27f893691eSAlice Ryhl /// The name of the miscdevice. 28f893691eSAlice Ryhl pub name: &'static CStr, 29f893691eSAlice Ryhl } 30f893691eSAlice Ryhl 31f893691eSAlice Ryhl impl MiscDeviceOptions { 32f893691eSAlice Ryhl /// Create a raw `struct miscdev` ready for registration. into_raw<T: MiscDevice>(self) -> bindings::miscdevice33f893691eSAlice Ryhl pub const fn into_raw<T: MiscDevice>(self) -> bindings::miscdevice { 34f893691eSAlice Ryhl // SAFETY: All zeros is valid for this C type. 35f893691eSAlice Ryhl let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; 36f893691eSAlice Ryhl result.minor = bindings::MISC_DYNAMIC_MINOR as _; 37f893691eSAlice Ryhl result.name = self.name.as_char_ptr(); 38*74fc3493SAlice Ryhl result.fops = MiscdeviceVTable::<T>::build(); 39f893691eSAlice Ryhl result 40f893691eSAlice Ryhl } 41f893691eSAlice Ryhl } 42f893691eSAlice Ryhl 43f893691eSAlice Ryhl /// A registration of a miscdevice. 44f893691eSAlice Ryhl /// 45f893691eSAlice Ryhl /// # Invariants 46f893691eSAlice Ryhl /// 47f893691eSAlice Ryhl /// `inner` is a registered misc device. 48f893691eSAlice Ryhl #[repr(transparent)] 49f893691eSAlice Ryhl #[pin_data(PinnedDrop)] 50f893691eSAlice Ryhl pub struct MiscDeviceRegistration<T> { 51f893691eSAlice Ryhl #[pin] 52f893691eSAlice Ryhl inner: Opaque<bindings::miscdevice>, 53f893691eSAlice Ryhl _t: PhantomData<T>, 54f893691eSAlice Ryhl } 55f893691eSAlice Ryhl 56f893691eSAlice Ryhl // SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called 57f893691eSAlice Ryhl // `misc_register`. 58f893691eSAlice Ryhl unsafe impl<T> Send for MiscDeviceRegistration<T> {} 59f893691eSAlice Ryhl // SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in 60f893691eSAlice Ryhl // parallel. 61f893691eSAlice Ryhl unsafe impl<T> Sync for MiscDeviceRegistration<T> {} 62f893691eSAlice Ryhl 63f893691eSAlice Ryhl impl<T: MiscDevice> MiscDeviceRegistration<T> { 64f893691eSAlice Ryhl /// Register a misc device. register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error>65f893691eSAlice Ryhl pub fn register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error> { 66f893691eSAlice Ryhl try_pin_init!(Self { 67f893691eSAlice Ryhl inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| { 68f893691eSAlice Ryhl // SAFETY: The initializer can write to the provided `slot`. 69f893691eSAlice Ryhl unsafe { slot.write(opts.into_raw::<T>()) }; 70f893691eSAlice Ryhl 71f893691eSAlice Ryhl // SAFETY: We just wrote the misc device options to the slot. The miscdevice will 72f893691eSAlice Ryhl // get unregistered before `slot` is deallocated because the memory is pinned and 73f893691eSAlice Ryhl // the destructor of this type deallocates the memory. 74f893691eSAlice Ryhl // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered 75f893691eSAlice Ryhl // misc device. 76f893691eSAlice Ryhl to_result(unsafe { bindings::misc_register(slot) }) 77f893691eSAlice Ryhl }), 78f893691eSAlice Ryhl _t: PhantomData, 79f893691eSAlice Ryhl }) 80f893691eSAlice Ryhl } 81f893691eSAlice Ryhl 82f893691eSAlice Ryhl /// Returns a raw pointer to the misc device. as_raw(&self) -> *mut bindings::miscdevice83f893691eSAlice Ryhl pub fn as_raw(&self) -> *mut bindings::miscdevice { 84f893691eSAlice Ryhl self.inner.get() 85f893691eSAlice Ryhl } 86284ae0beSLee Jones 87284ae0beSLee Jones /// Access the `this_device` field. device(&self) -> &Device88284ae0beSLee Jones pub fn device(&self) -> &Device { 89284ae0beSLee Jones // SAFETY: This can only be called after a successful register(), which always 90284ae0beSLee Jones // initialises `this_device` with a valid device. Furthermore, the signature of this 91284ae0beSLee Jones // function tells the borrow-checker that the `&Device` reference must not outlive the 92284ae0beSLee Jones // `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be 93284ae0beSLee Jones // before the underlying `struct miscdevice` is destroyed. 94284ae0beSLee Jones unsafe { Device::as_ref((*self.as_raw()).this_device) } 95284ae0beSLee Jones } 96f893691eSAlice Ryhl } 97f893691eSAlice Ryhl 98f893691eSAlice Ryhl #[pinned_drop] 99f893691eSAlice Ryhl impl<T> PinnedDrop for MiscDeviceRegistration<T> { drop(self: Pin<&mut Self>)100f893691eSAlice Ryhl fn drop(self: Pin<&mut Self>) { 101f893691eSAlice Ryhl // SAFETY: We know that the device is registered by the type invariants. 102f893691eSAlice Ryhl unsafe { bindings::misc_deregister(self.inner.get()) }; 103f893691eSAlice Ryhl } 104f893691eSAlice Ryhl } 105f893691eSAlice Ryhl 106f893691eSAlice Ryhl /// Trait implemented by the private data of an open misc device. 107f893691eSAlice Ryhl #[vtable] 10888441d5cSAlice Ryhl pub trait MiscDevice: Sized { 109f893691eSAlice Ryhl /// What kind of pointer should `Self` be wrapped in. 110f893691eSAlice Ryhl type Ptr: ForeignOwnable + Send + Sync; 111f893691eSAlice Ryhl 112f893691eSAlice Ryhl /// Called when the misc device is opened. 113f893691eSAlice Ryhl /// 114f893691eSAlice Ryhl /// The returned pointer will be stored as the private data for the file. open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>11588441d5cSAlice Ryhl fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>; 116f893691eSAlice Ryhl 117f893691eSAlice Ryhl /// Called when the misc device is released. release(device: Self::Ptr, _file: &File)1180d8a7c7bSAlice Ryhl fn release(device: Self::Ptr, _file: &File) { 119f893691eSAlice Ryhl drop(device); 120f893691eSAlice Ryhl } 121f893691eSAlice Ryhl 122f893691eSAlice Ryhl /// Handler for ioctls. 123f893691eSAlice Ryhl /// 124f893691eSAlice Ryhl /// The `cmd` argument is usually manipulated using the utilties in [`kernel::ioctl`]. 125f893691eSAlice Ryhl /// 126f893691eSAlice Ryhl /// [`kernel::ioctl`]: mod@crate::ioctl ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>127f893691eSAlice Ryhl fn ioctl( 128f893691eSAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 1290d8a7c7bSAlice Ryhl _file: &File, 130f893691eSAlice Ryhl _cmd: u32, 131f893691eSAlice Ryhl _arg: usize, 132f893691eSAlice Ryhl ) -> Result<isize> { 1334401565fSMiguel Ojeda build_error!(VTABLE_DEFAULT_ERROR) 134f893691eSAlice Ryhl } 135f893691eSAlice Ryhl 136f893691eSAlice Ryhl /// Handler for ioctls. 137f893691eSAlice Ryhl /// 138f893691eSAlice Ryhl /// Used for 32-bit userspace on 64-bit platforms. 139f893691eSAlice Ryhl /// 140f893691eSAlice Ryhl /// This method is optional and only needs to be provided if the ioctl relies on structures 141f893691eSAlice Ryhl /// that have different layout on 32-bit and 64-bit userspace. If no implementation is 142f893691eSAlice Ryhl /// provided, then `compat_ptr_ioctl` will be used instead. 143f893691eSAlice Ryhl #[cfg(CONFIG_COMPAT)] compat_ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>144f893691eSAlice Ryhl fn compat_ioctl( 145f893691eSAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 1460d8a7c7bSAlice Ryhl _file: &File, 147f893691eSAlice Ryhl _cmd: u32, 148f893691eSAlice Ryhl _arg: usize, 149f893691eSAlice Ryhl ) -> Result<isize> { 1504401565fSMiguel Ojeda build_error!(VTABLE_DEFAULT_ERROR) 151f893691eSAlice Ryhl } 1525bcc8bfeSAlice Ryhl 1535bcc8bfeSAlice Ryhl /// Show info for this fd. show_fdinfo( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _m: &SeqFile, _file: &File, )1545bcc8bfeSAlice Ryhl fn show_fdinfo( 1555bcc8bfeSAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 1565bcc8bfeSAlice Ryhl _m: &SeqFile, 1575bcc8bfeSAlice Ryhl _file: &File, 1585bcc8bfeSAlice Ryhl ) { 1592ab002c7SLinus Torvalds build_error!(VTABLE_DEFAULT_ERROR) 1605bcc8bfeSAlice Ryhl } 161f893691eSAlice Ryhl } 162f893691eSAlice Ryhl 163*74fc3493SAlice Ryhl /// A vtable for the file operations of a Rust miscdevice. 164*74fc3493SAlice Ryhl struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>); 165f893691eSAlice Ryhl 166*74fc3493SAlice Ryhl impl<T: MiscDevice> MiscdeviceVTable<T> { 167bd5ee6bcSAlice Ryhl /// # Safety 168bd5ee6bcSAlice Ryhl /// 169bd5ee6bcSAlice Ryhl /// `file` and `inode` must be the file and inode for a file that is undergoing initialization. 170bd5ee6bcSAlice Ryhl /// The file must be associated with a `MiscDeviceRegistration<T>`. open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int171*74fc3493SAlice Ryhl unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int { 172f893691eSAlice Ryhl // SAFETY: The pointers are valid and for a file being opened. 17388441d5cSAlice Ryhl let ret = unsafe { bindings::generic_file_open(inode, raw_file) }; 174f893691eSAlice Ryhl if ret != 0 { 175f893691eSAlice Ryhl return ret; 176f893691eSAlice Ryhl } 177f893691eSAlice Ryhl 17888441d5cSAlice Ryhl // SAFETY: The open call of a file can access the private data. 17988441d5cSAlice Ryhl let misc_ptr = unsafe { (*raw_file).private_data }; 18088441d5cSAlice Ryhl 18188441d5cSAlice Ryhl // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the 182*74fc3493SAlice Ryhl // associated `struct miscdevice` before calling into this method. Furthermore, 183*74fc3493SAlice Ryhl // `misc_open()` ensures that the miscdevice can't be unregistered and freed during this 184*74fc3493SAlice Ryhl // call to `fops_open`. 18588441d5cSAlice Ryhl let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() }; 18688441d5cSAlice Ryhl 1870d8a7c7bSAlice Ryhl // SAFETY: 18888441d5cSAlice Ryhl // * This underlying file is valid for (much longer than) the duration of `T::open`. 1890d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 19088441d5cSAlice Ryhl let file = unsafe { File::from_raw_file(raw_file) }; 19188441d5cSAlice Ryhl 19288441d5cSAlice Ryhl let ptr = match T::open(file, misc) { 193f893691eSAlice Ryhl Ok(ptr) => ptr, 194f893691eSAlice Ryhl Err(err) => return err.to_errno(), 195f893691eSAlice Ryhl }; 196f893691eSAlice Ryhl 197*74fc3493SAlice Ryhl // This overwrites the private data with the value specified by the user, changing the type 198*74fc3493SAlice Ryhl // of this file's private data. All future accesses to the private data is performed by 199*74fc3493SAlice Ryhl // other fops_* methods in this file, which all correctly cast the private data to the new 200*74fc3493SAlice Ryhl // type. 20188441d5cSAlice Ryhl // 20288441d5cSAlice Ryhl // SAFETY: The open call of a file can access the private data. 2032ab002c7SLinus Torvalds unsafe { (*raw_file).private_data = ptr.into_foreign() }; 204f893691eSAlice Ryhl 205f893691eSAlice Ryhl 0 206f893691eSAlice Ryhl } 207f893691eSAlice Ryhl 208bd5ee6bcSAlice Ryhl /// # Safety 209bd5ee6bcSAlice Ryhl /// 210*74fc3493SAlice Ryhl /// `file` and `inode` must be the file and inode for a file that is being released. The file 211*74fc3493SAlice Ryhl /// must be associated with a `MiscDeviceRegistration<T>`. release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int212*74fc3493SAlice Ryhl unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int { 213f893691eSAlice Ryhl // SAFETY: The release call of a file owns the private data. 214f893691eSAlice Ryhl let private = unsafe { (*file).private_data }; 215f893691eSAlice Ryhl // SAFETY: The release call of a file owns the private data. 216f893691eSAlice Ryhl let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) }; 217f893691eSAlice Ryhl 2180d8a7c7bSAlice Ryhl // SAFETY: 2190d8a7c7bSAlice Ryhl // * The file is valid for the duration of this call. 2200d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 2210d8a7c7bSAlice Ryhl T::release(ptr, unsafe { File::from_raw_file(file) }); 222f893691eSAlice Ryhl 223f893691eSAlice Ryhl 0 224f893691eSAlice Ryhl } 225f893691eSAlice Ryhl 226bd5ee6bcSAlice Ryhl /// # Safety 227bd5ee6bcSAlice Ryhl /// 228bd5ee6bcSAlice Ryhl /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long229*74fc3493SAlice Ryhl unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long { 230f893691eSAlice Ryhl // SAFETY: The ioctl call of a file can access the private data. 231f893691eSAlice Ryhl let private = unsafe { (*file).private_data }; 232f893691eSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 233f893691eSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 234f893691eSAlice Ryhl 2350d8a7c7bSAlice Ryhl // SAFETY: 2360d8a7c7bSAlice Ryhl // * The file is valid for the duration of this call. 2370d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 2380d8a7c7bSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 2390d8a7c7bSAlice Ryhl 2402ab002c7SLinus Torvalds match T::ioctl(device, file, cmd, arg) { 241f893691eSAlice Ryhl Ok(ret) => ret as c_long, 242f893691eSAlice Ryhl Err(err) => err.to_errno() as c_long, 243f893691eSAlice Ryhl } 244f893691eSAlice Ryhl } 245f893691eSAlice Ryhl 246bd5ee6bcSAlice Ryhl /// # Safety 247bd5ee6bcSAlice Ryhl /// 248bd5ee6bcSAlice Ryhl /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 249f893691eSAlice Ryhl #[cfg(CONFIG_COMPAT)] compat_ioctl( file: *mut bindings::file, cmd: c_uint, arg: c_ulong, ) -> c_long250*74fc3493SAlice Ryhl unsafe extern "C" fn compat_ioctl( 251f893691eSAlice Ryhl file: *mut bindings::file, 252f893691eSAlice Ryhl cmd: c_uint, 253f893691eSAlice Ryhl arg: c_ulong, 254f893691eSAlice Ryhl ) -> c_long { 255f893691eSAlice Ryhl // SAFETY: The compat ioctl call of a file can access the private data. 256f893691eSAlice Ryhl let private = unsafe { (*file).private_data }; 257f893691eSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 258f893691eSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 259f893691eSAlice Ryhl 2600d8a7c7bSAlice Ryhl // SAFETY: 2610d8a7c7bSAlice Ryhl // * The file is valid for the duration of this call. 2620d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 2630d8a7c7bSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 2640d8a7c7bSAlice Ryhl 2652ab002c7SLinus Torvalds match T::compat_ioctl(device, file, cmd, arg) { 266f893691eSAlice Ryhl Ok(ret) => ret as c_long, 267f893691eSAlice Ryhl Err(err) => err.to_errno() as c_long, 268f893691eSAlice Ryhl } 269f893691eSAlice Ryhl } 2705bcc8bfeSAlice Ryhl 2715bcc8bfeSAlice Ryhl /// # Safety 2725bcc8bfeSAlice Ryhl /// 2735bcc8bfeSAlice Ryhl /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 2745bcc8bfeSAlice Ryhl /// - `seq_file` must be a valid `struct seq_file` that we can write to. show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file)275*74fc3493SAlice Ryhl unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) { 2765bcc8bfeSAlice Ryhl // SAFETY: The release call of a file owns the private data. 2775bcc8bfeSAlice Ryhl let private = unsafe { (*file).private_data }; 2785bcc8bfeSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 2795bcc8bfeSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 2805bcc8bfeSAlice Ryhl // SAFETY: 2815bcc8bfeSAlice Ryhl // * The file is valid for the duration of this call. 2825bcc8bfeSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 2835bcc8bfeSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 284*74fc3493SAlice Ryhl // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in 285*74fc3493SAlice Ryhl // which this method is called. 2865bcc8bfeSAlice Ryhl let m = unsafe { SeqFile::from_raw(seq_file) }; 2875bcc8bfeSAlice Ryhl 2885bcc8bfeSAlice Ryhl T::show_fdinfo(device, m, file); 2895bcc8bfeSAlice Ryhl } 290*74fc3493SAlice Ryhl 291*74fc3493SAlice Ryhl const VTABLE: bindings::file_operations = bindings::file_operations { 292*74fc3493SAlice Ryhl open: Some(Self::open), 293*74fc3493SAlice Ryhl release: Some(Self::release), 294*74fc3493SAlice Ryhl unlocked_ioctl: if T::HAS_IOCTL { 295*74fc3493SAlice Ryhl Some(Self::ioctl) 296*74fc3493SAlice Ryhl } else { 297*74fc3493SAlice Ryhl None 298*74fc3493SAlice Ryhl }, 299*74fc3493SAlice Ryhl #[cfg(CONFIG_COMPAT)] 300*74fc3493SAlice Ryhl compat_ioctl: if T::HAS_COMPAT_IOCTL { 301*74fc3493SAlice Ryhl Some(Self::compat_ioctl) 302*74fc3493SAlice Ryhl } else if T::HAS_IOCTL { 303*74fc3493SAlice Ryhl Some(bindings::compat_ptr_ioctl) 304*74fc3493SAlice Ryhl } else { 305*74fc3493SAlice Ryhl None 306*74fc3493SAlice Ryhl }, 307*74fc3493SAlice Ryhl show_fdinfo: if T::HAS_SHOW_FDINFO { 308*74fc3493SAlice Ryhl Some(Self::show_fdinfo) 309*74fc3493SAlice Ryhl } else { 310*74fc3493SAlice Ryhl None 311*74fc3493SAlice Ryhl }, 312*74fc3493SAlice Ryhl // SAFETY: All zeros is a valid value for `bindings::file_operations`. 313*74fc3493SAlice Ryhl ..unsafe { MaybeUninit::zeroed().assume_init() } 314*74fc3493SAlice Ryhl }; 315*74fc3493SAlice Ryhl build() -> &'static bindings::file_operations316*74fc3493SAlice Ryhl const fn build() -> &'static bindings::file_operations { 317*74fc3493SAlice Ryhl &Self::VTABLE 318*74fc3493SAlice Ryhl } 319*74fc3493SAlice Ryhl } 320