1#![deny(missing_docs)]
5use fdf_component::DriverError;
8use fidl::{Persistable, Serializable};
9use fidl_next_fuchsia_hardware_platform_device as fpdev;
10use log::error;
11use mmio::region::MmioRegion;
12use mmio::vmo::{VmoMapping, VmoMemory};
13use std::future::Future;
14use zx_status::Status;
15
16pub trait PlatformDevice {
18 type Mmio;
20
21 fn map_mmio_by_id(&self, id: u32) -> impl Future<Output = Result<Self::Mmio, DriverError>>;
23
24 fn map_mmio_by_name(&self, name: &str)
26 -> impl Future<Output = Result<Self::Mmio, DriverError>>;
27
28 fn get_typed_metadata<T: Persistable + Serializable>(
30 &self,
31 ) -> impl Future<Output = Result<T, DriverError>>;
32
33 fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(
35 &self,
36 ) -> impl Future<Output = Result<T, DriverError>>;
37}
38
39impl PlatformDevice for fidl_next::Client<fpdev::Device> {
40 type Mmio = MmioRegion<VmoMemory>;
41
42 async fn map_mmio_by_id(&self, id: u32) -> Result<Self::Mmio, DriverError> {
43 let mmio = self.get_mmio_by_id(id).await??;
44 Ok(map_mmio(mmio)?)
45 }
46
47 async fn map_mmio_by_name(&self, name: &str) -> Result<Self::Mmio, DriverError> {
48 let mmio = self.get_mmio_by_name(name).await??;
49 Ok(map_mmio(mmio)?)
50 }
51
52 async fn get_typed_metadata<T: Persistable + Serializable>(&self) -> Result<T, DriverError> {
53 let name = T::SERIALIZABLE_NAME;
54 let metadata_res = self.get_metadata(name).await??;
55 fidl::unpersist(&metadata_res.metadata).map_err(|err| {
56 error!("Failed to parse pdev metadata: {err}");
57 DriverError::Status(Status::INVALID_ARGS)
58 })
59 }
60
61 async fn get_deserialized_metadata<T: serde::de::DeserializeOwned>(
62 &self,
63 ) -> Result<T, DriverError> {
64 let name = "fuchsia.driver.metadata.Dictionary";
65 let metadata_res = self.get_metadata(name).await??;
66 let dict: fidl_fuchsia_driver_metadata::Dictionary =
67 fidl::unpersist(&metadata_res.metadata).map_err(|err| {
68 error!("Failed to unpersist dictionary: {err}");
69 DriverError::Status(Status::INVALID_ARGS)
70 })?;
71 fdf_metadata::from_dictionary(dict).map_err(|err| {
72 error!("Failed to deserialize config from dictionary: {err:?}");
73 DriverError::Status(Status::INVALID_ARGS)
74 })
75 }
76}
77
78pub trait PdevExt {
81 fn connect_to_pdev(&self) -> Result<fidl_next::Client<fpdev::Device>, DriverError>;
83}
84
85impl PdevExt for fdf_component::DriverContext {
86 fn connect_to_pdev(&self) -> Result<fidl_next::Client<fpdev::Device>, DriverError> {
87 let service = self
88 .incoming
89 .service::<fdf_component::ServiceInstance<fpdev::Service>>()
90 .instance("pdev")
91 .connect_next()?;
92 let (client_end, server_end) = fidl_next::fuchsia::create_channel();
93 service.device(server_end)?;
94 Ok(client_end.spawn())
95 }
96}
97
98fn map_mmio(mmio: fpdev::Mmio) -> Result<MmioRegion<VmoMemory>, Status> {
99 let (Some(vmo), Some(offset), Some(size)) = (mmio.vmo, mmio.offset, mmio.size) else {
100 error!("Mmio device missing vmo, offset or size");
101 return Err(Status::INTERNAL);
102 };
103 let offset = offset as usize;
104 let size = size as usize;
105
106 let mmio = VmoMapping::map(offset, size, vmo).map_err(|err| {
107 error!("Failed to map Mmio memory for vmo: {err}");
108 Status::INTERNAL
109 })?;
110 Ok(mmio)
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use fidl_next::{Request, Responder};
117 use fidl_test_metadata::{IntMetadata, Metadata};
118 use fuchsia_async::Task;
119 use mmio::Mmio;
120 use std::collections::HashMap;
121 use zx::{Vmo, VmoOp};
122
123 struct TestServer {
124 mmios: Vec<(&'static str, Option<fpdev::Mmio>)>,
125 metadata: HashMap<&'static str, Vec<u8>>,
126 }
127
128 impl TestServer {
129 fn new() -> Self {
130 Self { mmios: Vec::new(), metadata: HashMap::new() }
131 }
132
133 fn append_mmio(&mut self, name: &'static str, vmo: Vmo, offset: usize, size: usize) {
134 self.mmios.push((
135 name,
136 Some(fpdev::Mmio {
137 offset: Some(offset as u64),
138 size: Some(size as u64),
139 vmo: Some(vmo),
140 }),
141 ));
142 }
143
144 fn set_typed_metadata<T: Persistable + Serializable>(&mut self, metadata: &T) {
145 let bytes = fidl::persist(metadata).unwrap();
146 self.metadata.insert(T::SERIALIZABLE_NAME, bytes);
147 }
148
149 fn take_mmio_by_id(&mut self, id: u32) -> Result<fpdev::Mmio, Status> {
150 self.mmios
151 .get_mut(id as usize)
152 .ok_or(Status::NOT_FOUND)?
153 .1
154 .take()
155 .ok_or(Status::ALREADY_BOUND)
156 }
157
158 fn take_mmio_by_name(&mut self, name: &str) -> Result<fpdev::Mmio, Status> {
159 self.mmios
160 .iter_mut()
161 .find(|(n, _)| *n == name)
162 .ok_or(Status::NOT_FOUND)?
163 .1
164 .take()
165 .ok_or(Status::ALREADY_BOUND)
166 }
167
168 fn read_metadata(&self, id: &str) -> Result<&[u8], Status> {
169 self.metadata.get(id).map(|v| v.as_slice()).ok_or(Status::NOT_FOUND)
170 }
171
172 fn run(
173 self,
174 ) -> (
175 fidl_next::Client<fpdev::Device>,
176 Task<Result<(), fidl_next::ProtocolError<zx::Status>>>,
177 ) {
178 let (client_end, server_end) = fidl_next::fuchsia::create_channel::<fpdev::Device>();
179 let client = client_end.spawn();
180 let server = Task::local(async move {
181 let dispatcher = fidl_next::ServerDispatcher::new(server_end);
182 dispatcher.run_local(self).await.map(|_| ())
183 });
184 (client, server)
185 }
186 }
187
188 impl fpdev::DeviceLocalServerHandler for TestServer {
189 async fn get_mmio_by_id(
190 &mut self,
191 request: Request<fpdev::device::GetMmioById>,
192 responder: Responder<fpdev::device::GetMmioById>,
193 ) {
194 let index = request.payload().index;
195 match self.take_mmio_by_id(index) {
196 Ok(mmio) => {
197 let _ = responder.respond(mmio).await;
198 }
199 Err(status) => {
200 let _ = responder.respond_err(status).await;
201 }
202 }
203 }
204
205 async fn get_mmio_by_name(
206 &mut self,
207 request: Request<fpdev::device::GetMmioByName>,
208 responder: Responder<fpdev::device::GetMmioByName>,
209 ) {
210 let name = &request.payload().name;
211 match self.take_mmio_by_name(name) {
212 Ok(mmio) => {
213 let _ = responder.respond(mmio).await;
214 }
215 Err(status) => {
216 let _ = responder.respond_err(status).await;
217 }
218 }
219 }
220
221 async fn get_interrupt_by_id(
222 &mut self,
223 _request: Request<fpdev::device::GetInterruptById>,
224 _responder: Responder<fpdev::device::GetInterruptById>,
225 ) {
226 unimplemented!("not used by tests");
227 }
228
229 async fn get_interrupt_by_name(
230 &mut self,
231 _request: Request<fpdev::device::GetInterruptByName>,
232 _responder: Responder<fpdev::device::GetInterruptByName>,
233 ) {
234 unimplemented!("not used by tests");
235 }
236
237 async fn get_bti_by_id(
238 &mut self,
239 _request: Request<fpdev::device::GetBtiById>,
240 _responder: Responder<fpdev::device::GetBtiById>,
241 ) {
242 unimplemented!("not used by tests");
243 }
244
245 async fn get_bti_by_name(
246 &mut self,
247 _request: Request<fpdev::device::GetBtiByName>,
248 _responder: Responder<fpdev::device::GetBtiByName>,
249 ) {
250 unimplemented!("not used by tests");
251 }
252
253 async fn get_smc_by_id(
254 &mut self,
255 _request: Request<fpdev::device::GetSmcById>,
256 _responder: Responder<fpdev::device::GetSmcById>,
257 ) {
258 unimplemented!("not used by tests");
259 }
260
261 async fn get_smc_by_name(
262 &mut self,
263 _request: Request<fpdev::device::GetSmcByName>,
264 _responder: Responder<fpdev::device::GetSmcByName>,
265 ) {
266 unimplemented!("not used by tests");
267 }
268
269 async fn get_power_configuration(
270 &mut self,
271 _responder: Responder<fpdev::device::GetPowerConfiguration>,
272 ) {
273 unimplemented!("not used by tests");
274 }
275
276 async fn get_node_device_info(
277 &mut self,
278 _responder: Responder<fpdev::device::GetNodeDeviceInfo>,
279 ) {
280 unimplemented!("not used by tests");
281 }
282
283 async fn get_board_info(&mut self, _responder: Responder<fpdev::device::GetBoardInfo>) {
284 unimplemented!("not used by tests");
285 }
286
287 async fn get_metadata(
288 &mut self,
289 request: Request<fpdev::device::GetMetadata>,
290 responder: Responder<fpdev::device::GetMetadata>,
291 ) {
292 let id = &request.payload().id;
293 match self.read_metadata(id) {
294 Ok(metadata) => {
295 let _ = responder.respond(metadata).await;
296 }
297 Err(status) => {
298 let _ = responder.respond_err(status).await;
299 }
300 }
301 }
302 }
303
304 #[fuchsia::test]
305 async fn test_pdev() {
306 let mut server = TestServer::new();
307
308 let vmo = Vmo::create(4096).unwrap();
309 vmo.op_range(VmoOp::ZERO, 0, 4096).unwrap();
310 server.append_mmio("zero", vmo, 0, 4096);
311
312 let vmo = Vmo::create(1024).unwrap();
314 for i in 0..256 {
315 vmo.write(&((i as u32).to_le_bytes()), (i * size_of::<u32>()) as u64).unwrap();
316 }
317 server.append_mmio("dev", vmo, 32 * size_of::<u32>(), 16);
318
319 server.set_typed_metadata(&Metadata {
320 test_field: Some("foo".to_string()),
321 ..Default::default()
322 });
323
324 let (client, server) = server.run();
325
326 let mmio = client.map_mmio_by_id(1).await.unwrap();
327 assert_eq!(
328 client.map_mmio_by_id(1).await.err().map(|e| e.log_to_status()),
329 Some(Status::ALREADY_BOUND)
330 );
331 assert_eq!(
332 client.map_mmio_by_id(2).await.err().map(|e| e.log_to_status()),
333 Some(Status::NOT_FOUND)
334 );
335 assert_eq!(
336 client.map_mmio_by_name("dev").await.err().map(|e| e.log_to_status()),
337 Some(Status::ALREADY_BOUND)
338 );
339
340 assert_eq!(mmio.load32(0), 32);
341
342 let mmio = client.map_mmio_by_name("zero").await.unwrap();
343 assert_eq!(mmio.len(), 4096);
344 assert_eq!(mmio.load64(128), 0);
345
346 assert_eq!(
347 client.get_typed_metadata::<Metadata>().await.unwrap(),
348 Metadata { test_field: Some("foo".to_string()), ..Default::default() }
349 );
350
351 assert_eq!(
352 client.get_typed_metadata::<IntMetadata>().await.err().map(|e| e.log_to_status()),
353 Some(Status::NOT_FOUND)
354 );
355
356 let _ = server.abort().await;
357 }
358}