starnix_modules_nanohub/
sysfs.rs1use crate::sysfs_errno;
6use std::marker::PhantomData;
7
8use fidl::endpoints::{DiscoverableProtocolMarker, ProtocolMarker};
9use fuchsia_component::client::connect_to_protocol_sync;
10use starnix_core::task::CurrentTask;
11use starnix_core::vfs::{
12 AppendLockGuard, FileObject, FileOps, FsNode, FsNodeOps, InputBuffer, OutputBuffer,
13 fileops_impl_noop_sync,
14};
15use starnix_core::{fileops_impl_seekable, fs_node_impl_not_dir};
16use starnix_logging::log_error;
17use starnix_sync::{FileOpsCore, Locked, Mutex};
18use starnix_uapi::errors::Errno;
19use starnix_uapi::open_flags::OpenFlags;
20use starnix_uapi::{errno, error};
21use zx;
22
23pub fn try_get<T>(o: Option<T>) -> Result<T, SysfsError> {
25 o.ok_or_else(|| {
26 log_error!("Missing expected value from Nanohub service method response.");
27 sysfs_errno!(EINVAL)
28 })
29}
30
31#[derive(Debug, PartialEq)]
33pub struct SysfsError(pub Errno);
34
35#[macro_export]
37macro_rules! sysfs_errno {
38 ($error_code:ident) => {
39 $crate::sysfs::SysfsError(errno!($error_code))
40 };
41}
42
43#[macro_export]
45macro_rules! sysfs_error {
46 ($error_code:ident) => {
47 Err($crate::sysfs::SysfsError(errno!($error_code)))
48 };
49}
50
51impl From<fidl::Error> for SysfsError {
52 fn from(value: fidl::Error) -> Self {
56 log_error!("FIDL error while calling service method: {value:?}");
57 sysfs_errno!(EINVAL)
58 }
59}
60
61impl From<i32> for SysfsError {
62 fn from(value: i32) -> Self {
66 let status = zx::Status::from_raw(value);
67 log_error!("Service method responded with an error: {status:?}");
68 sysfs_errno!(EINVAL)
69 }
70}
71
72pub trait SysfsOps<S: fidl::endpoints::SynchronousProxy>: Default + Send + Sync + 'static {
76 fn show(&self, _service: &S) -> Result<String, SysfsError> {
78 sysfs_error!(EINVAL)
79 }
80
81 fn store(&self, _service: &S, _value: String) -> Result<(), SysfsError> {
83 sysfs_error!(EINVAL)
84 }
85}
86
87enum SysfsContentsState {
88 Unarmed,
89 Armed(String),
90 Err(Errno),
91}
92
93impl AsRef<SysfsContentsState> for SysfsContentsState {
94 fn as_ref(&self) -> &Self {
95 &self
96 }
97}
98
99pub struct SysfsFile<P: ProtocolMarker, O: SysfsOps<P::SynchronousProxy>> {
101 sysfs_ops: Box<O>,
103
104 service: Mutex<P::SynchronousProxy>,
106
107 contents: Mutex<SysfsContentsState>,
114
115 _phantom: PhantomData<P>,
116}
117
118impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> SysfsFile<P, O> {
119 pub fn new(sysfs_ops: Box<O>) -> Result<Box<Self>, Errno> {
120 let service = connect_to_protocol_sync::<P>().map_err(|e| {
121 log_error!("Error connecting to service: {:?}", e);
122 errno!(EIO)
123 })?;
124
125 Ok(Box::new(SysfsFile {
126 sysfs_ops,
127 service: Mutex::new(service),
128 contents: Mutex::new(SysfsContentsState::Unarmed),
129 _phantom: PhantomData,
130 }))
131 }
132
133 fn rearm(&self) {
137 let op_result = self.sysfs_ops.show(&self.service.lock());
138 let mut contents_guard = self.contents.lock();
139 *contents_guard = match op_result {
140 Ok(value) => SysfsContentsState::Armed(value),
141 Err(error) => SysfsContentsState::Err(error.0),
142 }
143 }
144}
145
146impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> FileOps for SysfsFile<P, O> {
147 fileops_impl_seekable!();
148 fileops_impl_noop_sync!();
149
150 fn read(
151 &self,
152 _locked: &mut Locked<FileOpsCore>,
153 _file: &FileObject,
154 _current_task: &CurrentTask,
155 offset: usize,
156 data: &mut dyn OutputBuffer,
157 ) -> Result<usize, Errno> {
158 if offset == 0 {
160 self.rearm();
161 };
162
163 let contents_guard = self.contents.lock();
164
165 match contents_guard.as_ref() {
166 SysfsContentsState::Err(error) => Err(error.clone()),
167 SysfsContentsState::Unarmed => {
168 log_error!("Failed to read SysFS file with no contents");
169 error!(EINVAL)
170 }
171 SysfsContentsState::Armed(value) => {
172 let bytes = value.as_bytes();
178 let start = offset.min(bytes.len());
179 let end = (offset + data.available()).min(bytes.len());
180 data.write(&bytes[start..end])
181 }
182 }
183 }
184
185 fn write(
186 &self,
187 _locked: &mut Locked<FileOpsCore>,
188 _file: &FileObject,
189 _current_task: &CurrentTask,
190 _offset: usize,
191 data: &mut dyn InputBuffer,
192 ) -> Result<usize, Errno> {
193 data.read_all()
196 .and_then(|bytes| {
197 String::from_utf8(bytes)
198 .map_err(|e| {
199 log_error!("Failed convert to input buffer to string: {e:?}");
200 errno!(EINVAL)
201 })
202 .map(|v| (v.len(), v))
203 })
204 .and_then(|(num_bytes, value)| {
205 self.sysfs_ops
206 .store(&self.service.lock(), value)
207 .map(|_| num_bytes)
208 .map_err(|e| e.0)
209 })
210 }
211}
212
213pub struct SysfsNode<P: ProtocolMarker, O: SysfsOps<P::SynchronousProxy>> {
215 _phantom_p: PhantomData<P>,
216 _phantom_o: PhantomData<O>,
217}
218
219impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> SysfsNode<P, O> {
220 pub fn new() -> Self {
221 Self { _phantom_p: PhantomData, _phantom_o: PhantomData }
222 }
223}
224
225impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> FsNodeOps
226 for SysfsNode<P, O>
227{
228 fs_node_impl_not_dir!();
229
230 fn create_file_ops(
231 &self,
232 _locked: &mut Locked<FileOpsCore>,
233 _node: &FsNode,
234 _current_task: &CurrentTask,
235 _flags: OpenFlags,
236 ) -> Result<Box<dyn FileOps>, Errno> {
237 SysfsFile::<P, O>::new(Box::new(O::default())).map(|f| f as Box<dyn FileOps>)
238 }
239
240 fn truncate(
244 &self,
245 _locked: &mut Locked<FileOpsCore>,
246 _guard: &AppendLockGuard<'_>,
247 _node: &FsNode,
248 _current_task: &CurrentTask,
249 _length: u64,
250 ) -> Result<(), Errno> {
251 Ok(())
252 }
253}