driver_tools/subcommands/list/
mod.rs1pub mod args;
6
7use anyhow::{Context, Result};
8use args::ListCommand;
9use bind::debugger::debug_dump::dump_bind_rules;
10use fidl_fuchsia_driver_development as fdd;
11use futures::join;
12use std::collections::{HashMap, HashSet};
13use std::io::Write;
14
15pub async fn list(
16 cmd: ListCommand,
17 writer: &mut dyn Write,
18 driver_development_proxy: fdd::ManagerProxy,
19) -> Result<()> {
20 let empty: [String; 0] = [];
21 let driver_info = fuchsia_driver_dev::get_driver_info(&driver_development_proxy, &empty);
22
23 let driver_info = if cmd.loaded {
24 let device_info = fuchsia_driver_dev::get_device_info(
26 &driver_development_proxy,
27 &empty,
28 false,
29 );
30
31 let (driver_info, device_info) = join!(driver_info, device_info);
33
34 let loaded_driver_set: HashSet<String> = HashSet::from_iter(
35 device_info?.into_iter().filter_map(|device_info| device_info.bound_driver_url),
36 );
37
38 driver_info?
40 .into_iter()
41 .filter(|driver| {
42 let mut loaded = false;
43 if let Some(ref url) = driver.url {
44 if loaded_driver_set.contains(url) {
45 loaded = true
46 }
47 }
48 loaded
49 })
50 .collect()
51 } else {
52 driver_info.await.context("Failed to get driver info")?
53 };
54
55 if cmd.verbose {
56 let device_info = fuchsia_driver_dev::get_device_info(
58 &driver_development_proxy,
59 &empty,
60 false,
61 )
62 .await?;
63
64 let mut driver_to_devices = HashMap::<String, Vec<String>>::new();
65 for device in device_info.into_iter() {
66 let driver = device.bound_driver_url.clone();
67 let device_name = device.moniker.as_ref();
68 if let (Some(driver), Some(device_name)) = (driver, device_name) {
69 driver_to_devices
70 .entry(driver.to_string())
71 .and_modify(|v| v.push(device_name.to_string()))
72 .or_insert_with(|| vec![device_name.to_string()]);
73 }
74 }
75 let driver_to_devices = driver_to_devices;
76
77 for driver in driver_info {
78 if let Some(name) = driver.name {
79 writeln!(writer, "{0: <10}: {1}", "Name", name)?;
80 }
81 if let Some(url) = driver.url.as_ref() {
82 writeln!(writer, "{0: <10}: {1}", "URL", url)?;
83 }
84
85 writeln!(
87 writer,
88 "{0: <10}: {1}",
89 "DF Version",
90 driver.driver_framework_version.unwrap_or(1)
91 )?;
92
93 if let Some(device_categories) = driver.device_categories {
94 write!(writer, "Device Categories: [")?;
95
96 for (i, category_table) in device_categories.iter().enumerate() {
97 if let Some(category) = &category_table.category {
98 if let Some(subcategory) = &category_table.subcategory {
99 if !subcategory.is_empty() {
100 write!(writer, "{}::{}", category, subcategory)?;
101 } else {
102 write!(writer, "{}", category,)?;
103 }
104 } else {
105 write!(writer, "{}", category,)?;
106 }
107 }
108
109 if i != device_categories.len() - 1 {
110 write!(writer, ", ")?;
111 }
112 }
113 writeln!(writer, "]")?;
114 }
115
116 if let Some(url) = driver.url {
117 if let Some(devices) = driver_to_devices.get(&url) {
118 writeln!(writer, "{0: <10}:\n {1}", "Devices", devices.join("\n "))?;
119 }
120 }
121
122 let bind_rules =
124 driver.bind_rules_bytecode.map(|bytecode| dump_bind_rules(bytecode).ok()).flatten();
125 match bind_rules {
126 Some(bind_rules) => {
127 writeln!(writer, "{0: <10}: ", "Bind rules bytecode")?;
128 writeln!(writer, "{}", bind_rules)?;
129 }
130 _ => writeln!(writer, "Issue parsing the bind rules bytecode")?,
131 }
132 writeln!(writer)?;
133 }
134 } else {
135 for driver in driver_info {
136 if let Some(name) = driver.name {
137 let url = driver.url.unwrap_or_default();
138 writeln!(writer, "{:<20}: {}", name, url)?;
139 } else {
140 let url = driver.url.unwrap_or_default();
141 writeln!(writer, "{}", url)?;
142 }
143 }
144 }
145 Ok(())
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use argh::FromArgs;
152 use fidl::endpoints::ServerEnd;
153 use futures::future::{Future, FutureExt};
154 use futures::stream::StreamExt;
155 use {fidl_fuchsia_driver_framework as fdf, fuchsia_async as fasync};
156
157 async fn test_list<F, Fut>(cmd: ListCommand, on_driver_development_request: F) -> Result<String>
162 where
163 F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
164 Fut: Future<Output = Result<()>> + Send + Sync,
165 {
166 let (driver_development_proxy, mut driver_development_requests) =
167 fidl::endpoints::create_proxy_and_stream::<fdd::ManagerMarker>();
168
169 let mut writer = Vec::new();
171 let request_handler_task = fasync::Task::spawn(async move {
172 while let Some(res) = driver_development_requests.next().await {
173 let request = res.context("Failed to get next request")?;
174 on_driver_development_request(request).await.context("Failed to handle request")?;
175 }
176 anyhow::bail!("Driver development request stream unexpectedly closed");
177 });
178 futures::select! {
179 res = request_handler_task.fuse() => {
180 res?;
181 anyhow::bail!("Request handler task unexpectedly finished");
182 }
183 res = list(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List command failed")?,
184 }
185
186 String::from_utf8(writer).context("Failed to convert list output to a string")
187 }
188
189 async fn run_driver_info_iterator_server(
190 mut driver_infos: Vec<fdf::DriverInfo>,
191 iterator: ServerEnd<fdd::DriverInfoIteratorMarker>,
192 ) -> Result<()> {
193 let mut iterator = iterator.into_stream();
194 while let Some(res) = iterator.next().await {
195 let request = res.context("Failed to get request")?;
196 match request {
197 fdd::DriverInfoIteratorRequest::GetNext { responder } => {
198 responder
199 .send(&driver_infos)
200 .context("Failed to send driver infos to responder")?;
201 driver_infos.clear();
202 }
203 }
204 }
205 Ok(())
206 }
207
208 async fn run_device_info_iterator_server(
209 mut device_infos: Vec<fdd::NodeInfo>,
210 iterator: ServerEnd<fdd::NodeInfoIteratorMarker>,
211 ) -> Result<()> {
212 let mut iterator = iterator.into_stream();
213 while let Some(res) = iterator.next().await {
214 let request = res.context("Failed to get request")?;
215 match request {
216 fdd::NodeInfoIteratorRequest::GetNext { responder } => {
217 responder
218 .send(&device_infos)
219 .context("Failed to send device infos to responder")?;
220 device_infos.clear();
221 }
222 }
223 }
224 Ok(())
225 }
226
227 #[fasync::run_singlethreaded(test)]
228 async fn test_verbose() {
229 let cmd = ListCommand::from_args(&["list"], &["--verbose"]).unwrap();
230
231 let output = test_list(cmd, |request: fdd::ManagerRequest| async move {
232 match request {
233 fdd::ManagerRequest::GetDriverInfo {
234 driver_filter: _,
235 iterator,
236 control_handle: _,
237 } => run_driver_info_iterator_server(
238 vec![fdf::DriverInfo {
239 name: Some("foo".to_owned()),
240 url: Some("fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned()),
241 package_type: Some(fdf::DriverPackageType::Base),
242 device_categories: Some(vec![
243 fdf::DeviceCategory {
244 category: Some("connectivity".to_string()),
245 subcategory: Some("ethernet".to_string()),
246 ..Default::default()
247 },
248 fdf::DeviceCategory {
249 category: Some("usb".to_string()),
250 subcategory: None,
251 ..Default::default()
252 },
253 ]),
254 ..Default::default()
255 }],
256 iterator,
257 )
258 .await
259 .context("Failed to run driver info iterator server")?,
260 fdd::ManagerRequest::GetNodeInfo {
261 node_filter: _,
262 iterator,
263 control_handle: _,
264 exact_match: _,
265 } => run_device_info_iterator_server(
266 vec![fdd::NodeInfo {
267 bound_driver_url: Some(
268 "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
269 ),
270 moniker: Some("dev.sys.foo".to_owned()),
271 ..Default::default()
272 }],
273 iterator,
274 )
275 .await
276 .context("Failed to run device info iterator server")?,
277 _ => {}
278 }
279 Ok(())
280 })
281 .await
282 .unwrap();
283
284 println!("{}", output);
285
286 assert_eq!(
287 output,
288 r#"Name : foo
289URL : fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm
290DF Version: 1
291Device Categories: [connectivity::ethernet, usb]
292Devices :
293 dev.sys.foo
294Issue parsing the bind rules bytecode
295
296"#
297 );
298 }
299}