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 AppendLockWriteGuard, 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::{LockDepMutex, NanohubContentsLock, NanohubServiceLock};
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: LockDepMutex<P::SynchronousProxy, NanohubServiceLock>,
106
107 contents: LockDepMutex<SysfsContentsState, NanohubContentsLock>,
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: service.into(),
128 contents: SysfsContentsState::Unarmed.into(),
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 _file: &FileObject,
153 _current_task: &CurrentTask,
154 offset: usize,
155 data: &mut dyn OutputBuffer,
156 ) -> Result<usize, Errno> {
157 if offset == 0 {
159 self.rearm();
160 };
161
162 let contents_guard = self.contents.lock();
163
164 match contents_guard.as_ref() {
165 SysfsContentsState::Err(error) => Err(error.clone()),
166 SysfsContentsState::Unarmed => {
167 log_error!("Failed to read SysFS file with no contents");
168 error!(EINVAL)
169 }
170 SysfsContentsState::Armed(value) => {
171 let bytes = value.as_bytes();
177 let start = offset.min(bytes.len());
178 let end = (offset + data.available()).min(bytes.len());
179 data.write(&bytes[start..end])
180 }
181 }
182 }
183
184 fn write(
185 &self,
186 _file: &FileObject,
187 _current_task: &CurrentTask,
188 _offset: usize,
189 data: &mut dyn InputBuffer,
190 ) -> Result<usize, Errno> {
191 data.read_all()
194 .and_then(|bytes| {
195 String::from_utf8(bytes)
196 .map_err(|e| {
197 log_error!("Failed convert to input buffer to string: {e:?}");
198 errno!(EINVAL)
199 })
200 .map(|v| (v.len(), v))
201 })
202 .and_then(|(num_bytes, value)| {
203 self.sysfs_ops
204 .store(&self.service.lock(), value)
205 .map(|_| num_bytes)
206 .map_err(|e| e.0)
207 })
208 }
209}
210
211pub struct SysfsNode<P: ProtocolMarker, O: SysfsOps<P::SynchronousProxy>> {
213 _phantom_p: PhantomData<P>,
214 _phantom_o: PhantomData<O>,
215}
216
217impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> SysfsNode<P, O> {
218 pub fn new() -> Self {
219 Self { _phantom_p: PhantomData, _phantom_o: PhantomData }
220 }
221}
222
223impl<P: DiscoverableProtocolMarker, O: SysfsOps<P::SynchronousProxy>> FsNodeOps
224 for SysfsNode<P, O>
225{
226 fs_node_impl_not_dir!();
227
228 fn create_file_ops(
229 &self,
230 _node: &FsNode,
231 _current_task: &CurrentTask,
232 _flags: OpenFlags,
233 ) -> Result<Box<dyn FileOps>, Errno> {
234 SysfsFile::<P, O>::new(Box::new(O::default())).map(|f| f as Box<dyn FileOps>)
235 }
236
237 fn truncate(
241 &self,
242 _guard: &AppendLockWriteGuard<'_>,
243 _node: &FsNode,
244 _current_task: &CurrentTask,
245 _length: u64,
246 ) -> Result<(), Errno> {
247 Ok(())
248 }
249}