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, 17f8c78198SAlice Ryhl mm::virt::VmaNew, 18f893691eSAlice Ryhl prelude::*, 195bcc8bfeSAlice Ryhl seq_file::SeqFile, 20f893691eSAlice Ryhl str::CStr, 21f893691eSAlice Ryhl types::{ForeignOwnable, Opaque}, 22f893691eSAlice Ryhl }; 2327c7518eSMiguel Ojeda use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; 24f893691eSAlice Ryhl 25f893691eSAlice Ryhl /// Options for creating a misc device. 26f893691eSAlice Ryhl #[derive(Copy, Clone)] 27f893691eSAlice Ryhl pub struct MiscDeviceOptions { 28f893691eSAlice Ryhl /// The name of the miscdevice. 29f893691eSAlice Ryhl pub name: &'static CStr, 30f893691eSAlice Ryhl } 31f893691eSAlice Ryhl 32f893691eSAlice Ryhl impl MiscDeviceOptions { 33f893691eSAlice Ryhl /// Create a raw `struct miscdev` ready for registration. into_raw<T: MiscDevice>(self) -> bindings::miscdevice34f893691eSAlice Ryhl pub const fn into_raw<T: MiscDevice>(self) -> bindings::miscdevice { 35f893691eSAlice Ryhl // SAFETY: All zeros is valid for this C type. 36f893691eSAlice Ryhl let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; 37f893691eSAlice Ryhl result.minor = bindings::MISC_DYNAMIC_MINOR as _; 38f893691eSAlice Ryhl result.name = self.name.as_char_ptr(); 3974fc3493SAlice Ryhl result.fops = MiscdeviceVTable::<T>::build(); 40f893691eSAlice Ryhl result 41f893691eSAlice Ryhl } 42f893691eSAlice Ryhl } 43f893691eSAlice Ryhl 44f893691eSAlice Ryhl /// A registration of a miscdevice. 45f893691eSAlice Ryhl /// 46f893691eSAlice Ryhl /// # Invariants 47f893691eSAlice Ryhl /// 48f893691eSAlice Ryhl /// `inner` is a registered misc device. 49f893691eSAlice Ryhl #[repr(transparent)] 50f893691eSAlice Ryhl #[pin_data(PinnedDrop)] 51f893691eSAlice Ryhl pub struct MiscDeviceRegistration<T> { 52f893691eSAlice Ryhl #[pin] 53f893691eSAlice Ryhl inner: Opaque<bindings::miscdevice>, 54f893691eSAlice Ryhl _t: PhantomData<T>, 55f893691eSAlice Ryhl } 56f893691eSAlice Ryhl 57f893691eSAlice Ryhl // SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called 58f893691eSAlice Ryhl // `misc_register`. 59f893691eSAlice Ryhl unsafe impl<T> Send for MiscDeviceRegistration<T> {} 60f893691eSAlice Ryhl // SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in 61f893691eSAlice Ryhl // parallel. 62f893691eSAlice Ryhl unsafe impl<T> Sync for MiscDeviceRegistration<T> {} 63f893691eSAlice Ryhl 64f893691eSAlice Ryhl impl<T: MiscDevice> MiscDeviceRegistration<T> { 65f893691eSAlice Ryhl /// Register a misc device. register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error>66f893691eSAlice Ryhl pub fn register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error> { 67f893691eSAlice Ryhl try_pin_init!(Self { 68f893691eSAlice Ryhl inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| { 69f893691eSAlice Ryhl // SAFETY: The initializer can write to the provided `slot`. 70f893691eSAlice Ryhl unsafe { slot.write(opts.into_raw::<T>()) }; 71f893691eSAlice Ryhl 72f893691eSAlice Ryhl // SAFETY: We just wrote the misc device options to the slot. The miscdevice will 73f893691eSAlice Ryhl // get unregistered before `slot` is deallocated because the memory is pinned and 74f893691eSAlice Ryhl // the destructor of this type deallocates the memory. 75f893691eSAlice Ryhl // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered 76f893691eSAlice Ryhl // misc device. 77f893691eSAlice Ryhl to_result(unsafe { bindings::misc_register(slot) }) 78f893691eSAlice Ryhl }), 79f893691eSAlice Ryhl _t: PhantomData, 80f893691eSAlice Ryhl }) 81f893691eSAlice Ryhl } 82f893691eSAlice Ryhl 83f893691eSAlice Ryhl /// Returns a raw pointer to the misc device. as_raw(&self) -> *mut bindings::miscdevice84f893691eSAlice Ryhl pub fn as_raw(&self) -> *mut bindings::miscdevice { 85f893691eSAlice Ryhl self.inner.get() 86f893691eSAlice Ryhl } 87284ae0beSLee Jones 88284ae0beSLee Jones /// Access the `this_device` field. device(&self) -> &Device89284ae0beSLee Jones pub fn device(&self) -> &Device { 90284ae0beSLee Jones // SAFETY: This can only be called after a successful register(), which always 91284ae0beSLee Jones // initialises `this_device` with a valid device. Furthermore, the signature of this 92284ae0beSLee Jones // function tells the borrow-checker that the `&Device` reference must not outlive the 93284ae0beSLee Jones // `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be 94284ae0beSLee Jones // before the underlying `struct miscdevice` is destroyed. 95284ae0beSLee Jones unsafe { Device::as_ref((*self.as_raw()).this_device) } 96284ae0beSLee Jones } 97f893691eSAlice Ryhl } 98f893691eSAlice Ryhl 99f893691eSAlice Ryhl #[pinned_drop] 100f893691eSAlice Ryhl impl<T> PinnedDrop for MiscDeviceRegistration<T> { drop(self: Pin<&mut Self>)101f893691eSAlice Ryhl fn drop(self: Pin<&mut Self>) { 102f893691eSAlice Ryhl // SAFETY: We know that the device is registered by the type invariants. 103f893691eSAlice Ryhl unsafe { bindings::misc_deregister(self.inner.get()) }; 104f893691eSAlice Ryhl } 105f893691eSAlice Ryhl } 106f893691eSAlice Ryhl 107f893691eSAlice Ryhl /// Trait implemented by the private data of an open misc device. 108f893691eSAlice Ryhl #[vtable] 10988441d5cSAlice Ryhl pub trait MiscDevice: Sized { 110f893691eSAlice Ryhl /// What kind of pointer should `Self` be wrapped in. 111f893691eSAlice Ryhl type Ptr: ForeignOwnable + Send + Sync; 112f893691eSAlice Ryhl 113f893691eSAlice Ryhl /// Called when the misc device is opened. 114f893691eSAlice Ryhl /// 115f893691eSAlice Ryhl /// The returned pointer will be stored as the private data for the file. open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>11688441d5cSAlice Ryhl fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>; 117f893691eSAlice Ryhl 118f893691eSAlice Ryhl /// Called when the misc device is released. release(device: Self::Ptr, _file: &File)1190d8a7c7bSAlice Ryhl fn release(device: Self::Ptr, _file: &File) { 120f893691eSAlice Ryhl drop(device); 121f893691eSAlice Ryhl } 122f893691eSAlice Ryhl 123f8c78198SAlice Ryhl /// Handle for mmap. 124f8c78198SAlice Ryhl /// 125f8c78198SAlice Ryhl /// This function is invoked when a user space process invokes the `mmap` system call on 126f8c78198SAlice Ryhl /// `file`. The function is a callback that is part of the VMA initializer. The kernel will do 127f8c78198SAlice Ryhl /// initial setup of the VMA before calling this function. The function can then interact with 128f8c78198SAlice Ryhl /// the VMA initialization by calling methods of `vma`. If the function does not return an 129f8c78198SAlice Ryhl /// error, the kernel will complete initialization of the VMA according to the properties of 130f8c78198SAlice Ryhl /// `vma`. mmap( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _vma: &VmaNew, ) -> Result131f8c78198SAlice Ryhl fn mmap( 132f8c78198SAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 133f8c78198SAlice Ryhl _file: &File, 134f8c78198SAlice Ryhl _vma: &VmaNew, 135f8c78198SAlice Ryhl ) -> Result { 136f8c78198SAlice Ryhl build_error!(VTABLE_DEFAULT_ERROR) 137f8c78198SAlice Ryhl } 138f8c78198SAlice Ryhl 139f893691eSAlice Ryhl /// Handler for ioctls. 140f893691eSAlice Ryhl /// 141*81e9edc1SChristian Schrefl /// The `cmd` argument is usually manipulated using the utilities in [`kernel::ioctl`]. 142f893691eSAlice Ryhl /// 143f893691eSAlice Ryhl /// [`kernel::ioctl`]: mod@crate::ioctl ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>144f893691eSAlice Ryhl fn 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 } 152f893691eSAlice Ryhl 153f893691eSAlice Ryhl /// Handler for ioctls. 154f893691eSAlice Ryhl /// 155f893691eSAlice Ryhl /// Used for 32-bit userspace on 64-bit platforms. 156f893691eSAlice Ryhl /// 157f893691eSAlice Ryhl /// This method is optional and only needs to be provided if the ioctl relies on structures 158f893691eSAlice Ryhl /// that have different layout on 32-bit and 64-bit userspace. If no implementation is 159f893691eSAlice Ryhl /// provided, then `compat_ptr_ioctl` will be used instead. 160f893691eSAlice Ryhl #[cfg(CONFIG_COMPAT)] compat_ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>161f893691eSAlice Ryhl fn compat_ioctl( 162f893691eSAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 1630d8a7c7bSAlice Ryhl _file: &File, 164f893691eSAlice Ryhl _cmd: u32, 165f893691eSAlice Ryhl _arg: usize, 166f893691eSAlice Ryhl ) -> Result<isize> { 1674401565fSMiguel Ojeda build_error!(VTABLE_DEFAULT_ERROR) 168f893691eSAlice Ryhl } 1695bcc8bfeSAlice Ryhl 1705bcc8bfeSAlice Ryhl /// Show info for this fd. show_fdinfo( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _m: &SeqFile, _file: &File, )1715bcc8bfeSAlice Ryhl fn show_fdinfo( 1725bcc8bfeSAlice Ryhl _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 1735bcc8bfeSAlice Ryhl _m: &SeqFile, 1745bcc8bfeSAlice Ryhl _file: &File, 1755bcc8bfeSAlice Ryhl ) { 1762ab002c7SLinus Torvalds build_error!(VTABLE_DEFAULT_ERROR) 1775bcc8bfeSAlice Ryhl } 178f893691eSAlice Ryhl } 179f893691eSAlice Ryhl 18074fc3493SAlice Ryhl /// A vtable for the file operations of a Rust miscdevice. 18174fc3493SAlice Ryhl struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>); 182f893691eSAlice Ryhl 18374fc3493SAlice Ryhl impl<T: MiscDevice> MiscdeviceVTable<T> { 184bd5ee6bcSAlice Ryhl /// # Safety 185bd5ee6bcSAlice Ryhl /// 186bd5ee6bcSAlice Ryhl /// `file` and `inode` must be the file and inode for a file that is undergoing initialization. 187bd5ee6bcSAlice Ryhl /// The file must be associated with a `MiscDeviceRegistration<T>`. open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int18874fc3493SAlice Ryhl unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int { 189f893691eSAlice Ryhl // SAFETY: The pointers are valid and for a file being opened. 19088441d5cSAlice Ryhl let ret = unsafe { bindings::generic_file_open(inode, raw_file) }; 191f893691eSAlice Ryhl if ret != 0 { 192f893691eSAlice Ryhl return ret; 193f893691eSAlice Ryhl } 194f893691eSAlice Ryhl 19588441d5cSAlice Ryhl // SAFETY: The open call of a file can access the private data. 19688441d5cSAlice Ryhl let misc_ptr = unsafe { (*raw_file).private_data }; 19788441d5cSAlice Ryhl 19888441d5cSAlice Ryhl // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the 19974fc3493SAlice Ryhl // associated `struct miscdevice` before calling into this method. Furthermore, 20074fc3493SAlice Ryhl // `misc_open()` ensures that the miscdevice can't be unregistered and freed during this 20174fc3493SAlice Ryhl // call to `fops_open`. 20288441d5cSAlice Ryhl let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() }; 20388441d5cSAlice Ryhl 2040d8a7c7bSAlice Ryhl // SAFETY: 20588441d5cSAlice Ryhl // * This underlying file is valid for (much longer than) the duration of `T::open`. 2060d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 20788441d5cSAlice Ryhl let file = unsafe { File::from_raw_file(raw_file) }; 20888441d5cSAlice Ryhl 20988441d5cSAlice Ryhl let ptr = match T::open(file, misc) { 210f893691eSAlice Ryhl Ok(ptr) => ptr, 211f893691eSAlice Ryhl Err(err) => return err.to_errno(), 212f893691eSAlice Ryhl }; 213f893691eSAlice Ryhl 21474fc3493SAlice Ryhl // This overwrites the private data with the value specified by the user, changing the type 21574fc3493SAlice Ryhl // of this file's private data. All future accesses to the private data is performed by 21674fc3493SAlice Ryhl // other fops_* methods in this file, which all correctly cast the private data to the new 21774fc3493SAlice Ryhl // type. 21888441d5cSAlice Ryhl // 21988441d5cSAlice Ryhl // SAFETY: The open call of a file can access the private data. 2201a4736c3STamir Duberstein unsafe { (*raw_file).private_data = ptr.into_foreign().cast() }; 221f893691eSAlice Ryhl 222f893691eSAlice Ryhl 0 223f893691eSAlice Ryhl } 224f893691eSAlice Ryhl 225bd5ee6bcSAlice Ryhl /// # Safety 226bd5ee6bcSAlice Ryhl /// 22774fc3493SAlice Ryhl /// `file` and `inode` must be the file and inode for a file that is being released. The file 22874fc3493SAlice Ryhl /// must be associated with a `MiscDeviceRegistration<T>`. release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int22974fc3493SAlice Ryhl unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int { 230f893691eSAlice Ryhl // SAFETY: The release call of a file owns the private data. 2311a4736c3STamir Duberstein let private = unsafe { (*file).private_data }.cast(); 232f893691eSAlice Ryhl // SAFETY: The release call of a file owns the private data. 233f893691eSAlice Ryhl let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(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 T::release(ptr, unsafe { File::from_raw_file(file) }); 239f893691eSAlice Ryhl 240f893691eSAlice Ryhl 0 241f893691eSAlice Ryhl } 242f893691eSAlice Ryhl 243bd5ee6bcSAlice Ryhl /// # Safety 244bd5ee6bcSAlice Ryhl /// 245bd5ee6bcSAlice Ryhl /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 246f8c78198SAlice Ryhl /// `vma` must be a vma that is currently being mmap'ed with this file. mmap( file: *mut bindings::file, vma: *mut bindings::vm_area_struct, ) -> c_int247f8c78198SAlice Ryhl unsafe extern "C" fn mmap( 248f8c78198SAlice Ryhl file: *mut bindings::file, 249f8c78198SAlice Ryhl vma: *mut bindings::vm_area_struct, 250f8c78198SAlice Ryhl ) -> c_int { 251f8c78198SAlice Ryhl // SAFETY: The mmap call of a file can access the private data. 252f8c78198SAlice Ryhl let private = unsafe { (*file).private_data }; 253f8c78198SAlice Ryhl // SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and 254f8c78198SAlice Ryhl // `from_foreign` in `release`, and `fops_mmap` is guaranteed to be called between those 255f8c78198SAlice Ryhl // two operations. 256ec7714e4SLinus Torvalds let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private.cast()) }; 257f8c78198SAlice Ryhl // SAFETY: The caller provides a vma that is undergoing initial VMA setup. 258f8c78198SAlice Ryhl let area = unsafe { VmaNew::from_raw(vma) }; 259f8c78198SAlice Ryhl // SAFETY: 260f8c78198SAlice Ryhl // * The file is valid for the duration of this call. 261f8c78198SAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 262f8c78198SAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 263f8c78198SAlice Ryhl 264f8c78198SAlice Ryhl match T::mmap(device, file, area) { 265f8c78198SAlice Ryhl Ok(()) => 0, 266f8c78198SAlice Ryhl Err(err) => err.to_errno(), 267f8c78198SAlice Ryhl } 268f8c78198SAlice Ryhl } 269f8c78198SAlice Ryhl 270f8c78198SAlice Ryhl /// # Safety 271f8c78198SAlice Ryhl /// 272f8c78198SAlice 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_long27374fc3493SAlice Ryhl unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long { 274f893691eSAlice Ryhl // SAFETY: The ioctl call of a file can access the private data. 2751a4736c3STamir Duberstein let private = unsafe { (*file).private_data }.cast(); 276f893691eSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 277f893691eSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 278f893691eSAlice Ryhl 2790d8a7c7bSAlice Ryhl // SAFETY: 2800d8a7c7bSAlice Ryhl // * The file is valid for the duration of this call. 2810d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 2820d8a7c7bSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 2830d8a7c7bSAlice Ryhl 2842ab002c7SLinus Torvalds match T::ioctl(device, file, cmd, arg) { 285f893691eSAlice Ryhl Ok(ret) => ret as c_long, 286f893691eSAlice Ryhl Err(err) => err.to_errno() as c_long, 287f893691eSAlice Ryhl } 288f893691eSAlice Ryhl } 289f893691eSAlice Ryhl 290bd5ee6bcSAlice Ryhl /// # Safety 291bd5ee6bcSAlice Ryhl /// 292bd5ee6bcSAlice Ryhl /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 293f893691eSAlice Ryhl #[cfg(CONFIG_COMPAT)] compat_ioctl( file: *mut bindings::file, cmd: c_uint, arg: c_ulong, ) -> c_long29474fc3493SAlice Ryhl unsafe extern "C" fn compat_ioctl( 295f893691eSAlice Ryhl file: *mut bindings::file, 296f893691eSAlice Ryhl cmd: c_uint, 297f893691eSAlice Ryhl arg: c_ulong, 298f893691eSAlice Ryhl ) -> c_long { 299f893691eSAlice Ryhl // SAFETY: The compat ioctl call of a file can access the private data. 3001a4736c3STamir Duberstein let private = unsafe { (*file).private_data }.cast(); 301f893691eSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 302f893691eSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 303f893691eSAlice Ryhl 3040d8a7c7bSAlice Ryhl // SAFETY: 3050d8a7c7bSAlice Ryhl // * The file is valid for the duration of this call. 3060d8a7c7bSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 3070d8a7c7bSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 3080d8a7c7bSAlice Ryhl 3092ab002c7SLinus Torvalds match T::compat_ioctl(device, file, cmd, arg) { 310f893691eSAlice Ryhl Ok(ret) => ret as c_long, 311f893691eSAlice Ryhl Err(err) => err.to_errno() as c_long, 312f893691eSAlice Ryhl } 313f893691eSAlice Ryhl } 3145bcc8bfeSAlice Ryhl 3155bcc8bfeSAlice Ryhl /// # Safety 3165bcc8bfeSAlice Ryhl /// 3175bcc8bfeSAlice Ryhl /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 3185bcc8bfeSAlice 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)31974fc3493SAlice Ryhl unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) { 3205bcc8bfeSAlice Ryhl // SAFETY: The release call of a file owns the private data. 3211a4736c3STamir Duberstein let private = unsafe { (*file).private_data }.cast(); 3225bcc8bfeSAlice Ryhl // SAFETY: Ioctl calls can borrow the private data of the file. 3235bcc8bfeSAlice Ryhl let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 3245bcc8bfeSAlice Ryhl // SAFETY: 3255bcc8bfeSAlice Ryhl // * The file is valid for the duration of this call. 3265bcc8bfeSAlice Ryhl // * There is no active fdget_pos region on the file on this thread. 3275bcc8bfeSAlice Ryhl let file = unsafe { File::from_raw_file(file) }; 32874fc3493SAlice Ryhl // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in 32974fc3493SAlice Ryhl // which this method is called. 3305bcc8bfeSAlice Ryhl let m = unsafe { SeqFile::from_raw(seq_file) }; 3315bcc8bfeSAlice Ryhl 3325bcc8bfeSAlice Ryhl T::show_fdinfo(device, m, file); 3335bcc8bfeSAlice Ryhl } 33474fc3493SAlice Ryhl 33574fc3493SAlice Ryhl const VTABLE: bindings::file_operations = bindings::file_operations { 33674fc3493SAlice Ryhl open: Some(Self::open), 33774fc3493SAlice Ryhl release: Some(Self::release), 338f8c78198SAlice Ryhl mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None }, 33974fc3493SAlice Ryhl unlocked_ioctl: if T::HAS_IOCTL { 34074fc3493SAlice Ryhl Some(Self::ioctl) 34174fc3493SAlice Ryhl } else { 34274fc3493SAlice Ryhl None 34374fc3493SAlice Ryhl }, 34474fc3493SAlice Ryhl #[cfg(CONFIG_COMPAT)] 34574fc3493SAlice Ryhl compat_ioctl: if T::HAS_COMPAT_IOCTL { 34674fc3493SAlice Ryhl Some(Self::compat_ioctl) 34774fc3493SAlice Ryhl } else if T::HAS_IOCTL { 34874fc3493SAlice Ryhl Some(bindings::compat_ptr_ioctl) 34974fc3493SAlice Ryhl } else { 35074fc3493SAlice Ryhl None 35174fc3493SAlice Ryhl }, 35274fc3493SAlice Ryhl show_fdinfo: if T::HAS_SHOW_FDINFO { 35374fc3493SAlice Ryhl Some(Self::show_fdinfo) 35474fc3493SAlice Ryhl } else { 35574fc3493SAlice Ryhl None 35674fc3493SAlice Ryhl }, 35774fc3493SAlice Ryhl // SAFETY: All zeros is a valid value for `bindings::file_operations`. 35874fc3493SAlice Ryhl ..unsafe { MaybeUninit::zeroed().assume_init() } 35974fc3493SAlice Ryhl }; 36074fc3493SAlice Ryhl build() -> &'static bindings::file_operations36174fc3493SAlice Ryhl const fn build() -> &'static bindings::file_operations { 36274fc3493SAlice Ryhl &Self::VTABLE 36374fc3493SAlice Ryhl } 36474fc3493SAlice Ryhl } 365