starnix_core/device/
kobject.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::device::DeviceMode;
6use crate::task::CurrentTask;
7use crate::vfs::buffers::{InputBuffer, OutputBuffer};
8use crate::vfs::pseudo::simple_directory::SimpleDirectory;
9use crate::vfs::{
10    FileObject, FileOps, FsNode, FsNodeOps, FsString, PathBuilder, fileops_impl_noop_sync,
11    fileops_impl_seekable, fs_node_impl_not_dir,
12};
13use starnix_logging::track_stub;
14use starnix_sync::{FileOpsCore, Locked};
15use starnix_uapi::device_type::DeviceType;
16use starnix_uapi::errors::Errno;
17use starnix_uapi::open_flags::OpenFlags;
18use starnix_uapi::{errno, error};
19use std::sync::Arc;
20
21/// A Class is a higher-level view of a device.
22///
23/// It groups devices based on what they do, rather than how they are connected.
24#[derive(Clone)]
25pub struct Class {
26    pub name: FsString,
27    pub dir: Arc<SimpleDirectory>,
28    /// Physical bus that the devices belong to.
29    pub bus: Bus,
30    pub collection: Arc<SimpleDirectory>,
31}
32
33impl Class {
34    pub fn new(
35        name: FsString,
36        dir: Arc<SimpleDirectory>,
37        bus: Bus,
38        collection: Arc<SimpleDirectory>,
39    ) -> Self {
40        Self { name, dir, bus, collection }
41    }
42}
43
44impl std::fmt::Debug for Class {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_struct("Class").field("name", &self.name).field("bus", &self.bus).finish()
47    }
48}
49
50/// A Bus identifies how the devices are connected to the processor.
51#[derive(Clone)]
52pub struct Bus {
53    pub name: FsString,
54    pub dir: Arc<SimpleDirectory>,
55    pub collection: Option<Arc<SimpleDirectory>>,
56}
57
58impl Bus {
59    pub fn new(
60        name: FsString,
61        dir: Arc<SimpleDirectory>,
62        collection: Option<Arc<SimpleDirectory>>,
63    ) -> Self {
64        Self { name, dir, collection }
65    }
66}
67
68impl std::fmt::Debug for Bus {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.debug_struct("Bus").field("name", &self.name).finish()
71    }
72}
73
74#[derive(Clone, Debug)]
75pub struct Device {
76    pub name: FsString,
77    pub class: Class,
78    pub metadata: Option<DeviceMetadata>,
79}
80
81impl Device {
82    pub fn new(name: FsString, class: Class, metadata: Option<DeviceMetadata>) -> Self {
83        Self { name, class, metadata }
84    }
85
86    /// Returns a path to the device, relative to the sysfs root, going up `depth` directories.
87    pub fn path_from_depth(&self, depth: usize) -> FsString {
88        let mut builder = PathBuilder::new();
89        builder.prepend_element(self.name.as_ref());
90        builder.prepend_element(self.class.name.as_ref());
91        builder.prepend_element(self.class.bus.name.as_ref());
92        builder.prepend_element(b"devices".into());
93        for _ in 0..depth {
94            builder.prepend_element(b"..".into());
95        }
96        builder.build_relative()
97    }
98
99    pub fn uevent_properties(&self, separator: char) -> String {
100        let Some(metadata) = &self.metadata else {
101            return String::new();
102        };
103        // TODO(https://fxbug.dev/42078277): Pass the synthetic UUID when available.
104        // Otherwise, default as "0".
105        let path = self.path_from_depth(0);
106        format!(
107            "DEVPATH=/{path}{separator}\
108                            DEVNAME={name}{separator}\
109                            SUBSYSTEM={subsystem}{separator}\
110                            SYNTH_UUID=0{separator}\
111                            MAJOR={major}{separator}\
112                            MINOR={minor}{separator}",
113            name = metadata.devname,
114            subsystem = self.class.name,
115            major = metadata.device_type.major(),
116            minor = metadata.device_type.minor(),
117        )
118    }
119}
120
121#[derive(Clone, Debug, PartialEq)]
122pub struct DeviceMetadata {
123    /// Name of the device in /dev.
124    ///
125    /// Also appears in sysfs via uevent.
126    pub devname: FsString,
127    pub device_type: DeviceType,
128    pub mode: DeviceMode,
129}
130
131impl DeviceMetadata {
132    pub fn new(devname: FsString, device_type: DeviceType, mode: DeviceMode) -> Self {
133        Self { devname, device_type, mode }
134    }
135}
136
137pub struct UEventFsNode {
138    device: Device,
139}
140
141impl UEventFsNode {
142    pub fn new(device: Device) -> Self {
143        Self { device }
144    }
145}
146
147impl FsNodeOps for UEventFsNode {
148    fs_node_impl_not_dir!();
149
150    fn create_file_ops(
151        &self,
152        _locked: &mut Locked<FileOpsCore>,
153        _node: &FsNode,
154        _current_task: &CurrentTask,
155        _flags: OpenFlags,
156    ) -> Result<Box<dyn FileOps>, Errno> {
157        Ok(Box::new(UEventFile::new(self.device.clone())))
158    }
159}
160
161struct UEventFile {
162    device: Device,
163}
164
165impl UEventFile {
166    pub fn new(device: Device) -> Self {
167        Self { device }
168    }
169
170    fn parse_commands(data: &[u8]) -> Vec<&[u8]> {
171        data.split(|&c| c == b'\0' || c == b'\n').collect()
172    }
173}
174
175impl FileOps for UEventFile {
176    fileops_impl_seekable!();
177    fileops_impl_noop_sync!();
178
179    fn read(
180        &self,
181        _locked: &mut Locked<FileOpsCore>,
182        _file: &FileObject,
183        _current_task: &CurrentTask,
184        offset: usize,
185        data: &mut dyn OutputBuffer,
186    ) -> Result<usize, Errno> {
187        let content = self.device.uevent_properties('\n');
188        data.write(content.get(offset..).ok_or_else(|| errno!(EINVAL))?.as_bytes())
189    }
190
191    fn write(
192        &self,
193        locked: &mut Locked<FileOpsCore>,
194        _file: &FileObject,
195        current_task: &CurrentTask,
196        offset: usize,
197        data: &mut dyn InputBuffer,
198    ) -> Result<usize, Errno> {
199        if offset != 0 {
200            return error!(EINVAL);
201        }
202        let content = data.read_all()?;
203        for command in Self::parse_commands(&content) {
204            // Ignore empty lines.
205            if command == b"" {
206                continue;
207            }
208
209            match UEventAction::try_from(command) {
210                Ok(c) => current_task.kernel().device_registry.dispatch_uevent(
211                    locked,
212                    c,
213                    self.device.clone(),
214                ),
215                Err(e) => {
216                    track_stub!(TODO("https://fxbug.dev/297435061"), "synthetic uevent variables");
217                    return Err(e);
218                }
219            }
220        }
221        Ok(content.len())
222    }
223}
224
225#[derive(Copy, Clone, Eq, PartialEq, Debug)]
226pub enum UEventAction {
227    Add,
228    Remove,
229    Change,
230    Move,
231    Online,
232    Offline,
233    Bind,
234    Unbind,
235}
236
237impl std::fmt::Display for UEventAction {
238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239        match self {
240            UEventAction::Add => write!(f, "add"),
241            UEventAction::Remove => write!(f, "remove"),
242            UEventAction::Change => write!(f, "change"),
243            UEventAction::Move => write!(f, "move"),
244            UEventAction::Online => write!(f, "online"),
245            UEventAction::Offline => write!(f, "offline"),
246            UEventAction::Bind => write!(f, "bind"),
247            UEventAction::Unbind => write!(f, "unbind"),
248        }
249    }
250}
251
252impl TryFrom<&[u8]> for UEventAction {
253    type Error = Errno;
254
255    fn try_from(action: &[u8]) -> Result<Self, Self::Error> {
256        match action {
257            b"add" => Ok(UEventAction::Add),
258            b"remove" => Ok(UEventAction::Remove),
259            b"change" => Ok(UEventAction::Change),
260            b"move" => Ok(UEventAction::Move),
261            b"online" => Ok(UEventAction::Online),
262            b"offline" => Ok(UEventAction::Offline),
263            b"bind" => Ok(UEventAction::Bind),
264            b"unbind" => Ok(UEventAction::Unbind),
265            _ => error!(EINVAL),
266        }
267    }
268}
269
270#[derive(Copy, Clone)]
271pub struct UEventContext {
272    pub seqnum: u64,
273}