summaryrefslogtreecommitdiff
path: root/rust/kernel/faux.rs
blob: cd4198fbb23228515923b29052ab80551062fd00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: GPL-2.0-only

//! Abstractions for the faux bus.
//!
//! This module provides bindings for working with faux devices in kernel modules.
//!
//! C header: [`include/linux/device/faux.h`](srctree/include/linux/device/faux.h)

use crate::{
    bindings,
    device,
    prelude::*,
    types::Opaque, //
};
use core::{
    marker::PhantomData,
    ptr::{
        null,
        null_mut,
        NonNull, //
    },
};

/// A faux device.
///
/// A faux device is a virtual device backed by the faux bus, primarily used for scenarios where a
/// real hardware device is not available or for testing.
///
/// # Invariants
///
/// The underlying `struct faux_device` is valid.
#[repr(transparent)]
pub struct Device<Ctx: device::DeviceContext = device::Normal>(
    Opaque<bindings::faux_device>,
    PhantomData<Ctx>,
);

impl<Ctx: device::DeviceContext> Device<Ctx> {
    #[inline]
    fn as_raw(&self) -> *mut bindings::faux_device {
        self.0.get()
    }

    /// # Safety
    ///
    /// `ptr` must be a valid pointer to a `struct faux_device`.
    #[inline]
    unsafe fn from_raw<'a>(ptr: *mut bindings::faux_device) -> &'a Self {
        // SAFETY: `Device` is a transparent wrapper of `Opaque<bindings::faux_device>`.
        unsafe { &*ptr.cast() }
    }
}

impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
    #[inline]
    fn as_ref(&self) -> &device::Device<Ctx> {
        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
        // `struct faux_device`. `dev` points to a valid `struct device`.
        unsafe { device::Device::from_raw(&raw mut (*self.as_raw()).dev) }
    }
}

// SAFETY: `faux::Device` is a transparent wrapper of `struct faux_device`.
// The offset is guaranteed to point to a valid device field inside `faux::Device`.
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
    const OFFSET: usize = core::mem::offset_of!(bindings::faux_device, dev);
}

/// The registration of a faux device.
///
/// This type represents the registration of a [`struct faux_device`]. When an instance of this type
/// is dropped, its respective faux device will be unregistered from the system.
///
/// # Invariants
///
/// - `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
/// - This object is proof that the object described by this `Registration` is bound to a device.
///
/// [`struct faux_device`]: srctree/include/linux/device/faux.h
pub struct Registration(NonNull<bindings::faux_device>);

impl Registration {
    /// Create and register a new faux device with the given name.
    #[inline]
    pub fn new(name: &CStr, parent: Option<&device::Device>) -> Result<Self> {
        // SAFETY:
        // - `name` is copied by this function into its own storage
        // - `faux_ops` is safe to leave NULL according to the C API
        // - `parent` can be either NULL or a pointer to a `struct device`, and `faux_device_create`
        //   will take a reference to `parent` using `device_add` - ensuring that it remains valid
        //   for the lifetime of the faux device.
        let dev = unsafe {
            bindings::faux_device_create(
                name.as_char_ptr(),
                parent.map_or(null_mut(), |p| p.as_raw()),
                null(),
            )
        };

        // The above function will return either a valid device, or NULL on failure
        // INVARIANT: The device will remain registered until faux_device_destroy() is called, which
        // happens in our Drop implementation.
        Ok(Self(NonNull::new(dev).ok_or(ENODEV)?))
    }

    fn as_raw(&self) -> *mut bindings::faux_device {
        self.0.as_ptr()
    }
}

impl AsRef<Device<device::Bound>> for Registration {
    #[inline]
    fn as_ref(&self) -> &Device<device::Bound> {
        // SAFETY:
        // - The underlying `struct faux_device` is guaranteed by the C API to be a valid
        //   initialized `device`.
        // - `faux_match()` always returns 1, and probe runs synchronously
        //   (PROBE_FORCE_SYNCHRONOUS).
        // - `suppress_bind_attrs = true` on faux_driver prevents userspace-triggered unbind via
        //   sysfs.
        // - `mem::forget(Registration)` is not a problem; if the `Registration` is leaked, the faux
        //   device stays bound forever.
        unsafe { Device::from_raw(self.as_raw()) }
    }
}

impl Drop for Registration {
    #[inline]
    fn drop(&mut self) {
        // SAFETY: `self.0` is a valid registered faux_device via our type invariants.
        unsafe { bindings::faux_device_destroy(self.as_raw()) }
    }
}

// SAFETY: The faux device API is thread-safe as guaranteed by the device core, as long as
// faux_device_destroy() is guaranteed to only be called once - which is guaranteed by our type not
// having Copy/Clone.
unsafe impl Send for Registration {}

// SAFETY: The faux device API is thread-safe as guaranteed by the device core, as long as
// faux_device_destroy() is guaranteed to only be called once - which is guaranteed by our type not
// having Copy/Clone.
unsafe impl Sync for Registration {}