1use 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#[derive(Clone)]
25pub struct Class {
26 pub name: FsString,
27 pub dir: Arc<SimpleDirectory>,
28 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#[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 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 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 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 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}