fuchsia_driver_dev/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context, Result, anyhow, format_err};
6use {fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf};
7
8#[derive(Debug)]
9pub struct Device(pub fdd::NodeInfo);
10
11impl Device {
12    /// Gets the full moniker name of the device.
13    pub fn get_moniker(&self) -> Result<&str> {
14        let moniker = self.0.moniker.as_ref();
15        Ok(moniker.ok_or_else(|| format_err!("Missing moniker"))?)
16    }
17
18    /// Gets the full identifying path name of the device.
19    pub fn get_full_name(&self) -> Result<&str> {
20        self.get_moniker()
21    }
22
23    /// Gets the last ordinal of the device's moniker.
24    ///
25    /// For a `moniker` value of "this.is.a.moniker.foo.bar", "bar" will be returned.
26    pub fn extract_name(&self) -> Result<&str> {
27        let moniker = self.get_moniker()?;
28        let (_, name) = moniker.rsplit_once('.').unwrap_or(("", &moniker));
29        Ok(name)
30    }
31}
32
33impl std::convert::From<fdd::NodeInfo> for Device {
34    fn from(device_info: fdd::NodeInfo) -> Device {
35        Device(device_info)
36    }
37}
38
39/// Combines pagination results into a single vector.
40pub async fn get_device_info(
41    service: &fdd::ManagerProxy,
42    device_filter: &[String],
43    exact_match: bool,
44) -> Result<Vec<fdd::NodeInfo>> {
45    let (iterator, iterator_server) =
46        fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>();
47
48    service
49        .get_node_info(device_filter, iterator_server, exact_match)
50        .context("FIDL call to get device info failed")?;
51
52    let mut info_result = Vec::new();
53
54    'outer: loop {
55        // To minimize round trips we request several results in one go.
56        let device_info_futures = vec![
57            iterator.get_next(),
58            iterator.get_next(),
59            iterator.get_next(),
60            iterator.get_next(),
61            iterator.get_next(),
62            iterator.get_next(),
63            iterator.get_next(),
64            iterator.get_next(),
65            iterator.get_next(),
66            iterator.get_next(),
67            iterator.get_next(),
68            iterator.get_next(),
69        ];
70        let device_info_result = futures::future::join_all(device_info_futures).await;
71        for result in device_info_result {
72            let mut device_info = result.context("FIDL call to get device info failed")?;
73            if device_info.len() == 0 {
74                break 'outer;
75            }
76            info_result.append(&mut device_info)
77        }
78    }
79    Ok(info_result)
80}
81
82/// Combines pagination results into a single vector.
83pub async fn get_driver_info(
84    service: &fdd::ManagerProxy,
85    driver_filter: &[String],
86) -> Result<Vec<fdf::DriverInfo>> {
87    let (iterator, iterator_server) =
88        fidl::endpoints::create_proxy::<fdd::DriverInfoIteratorMarker>();
89
90    service
91        .get_driver_info(driver_filter, iterator_server)
92        .context("FIDL call to get driver info failed")?;
93
94    let mut info_result = Vec::new();
95    'outer: loop {
96        // To minimize round trips we request several results in one go.
97        let driver_info_futures = vec![
98            iterator.get_next(),
99            iterator.get_next(),
100            iterator.get_next(),
101            iterator.get_next(),
102            iterator.get_next(),
103            iterator.get_next(),
104            iterator.get_next(),
105            iterator.get_next(),
106        ];
107        let driver_info_result = futures::future::join_all(driver_info_futures).await;
108        for result in driver_info_result {
109            let mut driver_info = result.context("FIDL call to get driver info failed")?;
110            if driver_info.len() == 0 {
111                break 'outer;
112            }
113            info_result.append(&mut driver_info)
114        }
115    }
116    Ok(info_result)
117}
118
119/// Combines pagination results into a single vector.
120pub async fn get_composite_node_specs(
121    service: &fdd::ManagerProxy,
122    name_filter: Option<String>,
123) -> Result<Vec<fdf::CompositeInfo>> {
124    let (iterator, iterator_server) =
125        fidl::endpoints::create_proxy::<fdd::CompositeNodeSpecIteratorMarker>();
126
127    service
128        .get_composite_node_specs(name_filter.as_deref(), iterator_server)
129        .context("FIDL call to get node groups failed")?;
130
131    let mut info_result = Vec::new();
132    loop {
133        let mut node_groups =
134            iterator.get_next().await.context("FIDL call to get node groups failed")?;
135        if node_groups.is_empty() {
136            break;
137        }
138        info_result.append(&mut node_groups)
139    }
140    Ok(info_result)
141}
142
143/// Gets the desired DriverInfo instance.
144///
145/// Filter based on the driver's URL.
146/// For example: "fuchsia-boot://domain/#meta/foo.cm"
147///
148/// # Arguments
149/// * `driver_filter` - Filter to the driver that matches the given filter.
150pub async fn get_driver_by_filter(
151    driver_filter: &String,
152    driver_development_proxy: &fdd::ManagerProxy,
153) -> Result<fdf::DriverInfo> {
154    let filter_list: [String; 1] = [driver_filter.to_string()];
155    let driver_list = get_driver_info(&driver_development_proxy, &filter_list).await?;
156    if driver_list.len() != 1 {
157        return Err(anyhow!(
158            "There should be exactly one match for '{}'. Found {}.",
159            driver_filter,
160            driver_list.len()
161        ));
162    }
163    let mut driver_info: Option<fdf::DriverInfo> = None;
164
165    // Confirm this is the correct match.
166    let driver = &driver_list[0];
167    if let Some(ref url) = driver.url {
168        if url == driver_filter {
169            driver_info = Some(driver.clone());
170        }
171    }
172    match driver_info {
173        Some(driver) => Ok(driver),
174        _ => Err(anyhow!("Did not find matching driver for: {}", driver_filter)),
175    }
176}
177
178/// Gets the driver that is bound to the given device.
179///
180/// Is able to fuzzy match on the device's topological path, where the shortest match
181/// will be the one chosen.
182///
183/// # Arguments
184/// * `device_topo_path` - The device's topological path. e.g. sys/platform/.../device
185pub async fn get_driver_by_device(
186    device_topo_path: &String,
187    driver_development_proxy: &fdd::ManagerProxy,
188) -> Result<fdf::DriverInfo> {
189    let device_filter: [String; 1] = [device_topo_path.to_string()];
190    let mut device_list =
191        get_device_info(&driver_development_proxy, &device_filter, /* exact_match= */ true).await?;
192    if device_list.len() != 1 {
193        let fuzzy_device_list = get_device_info(
194            &driver_development_proxy,
195            &device_filter,
196            /* exact_match= */ false,
197        )
198        .await?;
199        if fuzzy_device_list.len() == 0 {
200            return Err(anyhow!("No devices matched the query: {}", device_topo_path));
201        } else if fuzzy_device_list.len() > 1 {
202            let mut builder = "Found multiple matches. Did you mean one of these?\n\n".to_string();
203            for item in fuzzy_device_list {
204                let device: Device = item.into();
205                // We don't appear to have a string builder crate in-tree.
206                builder = format!("{}{}\n", builder, device.get_full_name()?);
207            }
208            return Err(anyhow!(builder));
209        }
210        device_list = fuzzy_device_list;
211    }
212
213    let found_device = device_list.remove(0);
214    match found_device.bound_driver_url {
215        Some(ref driver_filter) => {
216            get_driver_by_filter(&driver_filter, &driver_development_proxy).await
217        }
218        _ => Err(anyhow!("Did not find driver for device {}", &device_topo_path)),
219    }
220}
221
222/// Gets the devices that are bound to the given driver.
223///
224/// Filter based on the driver's URL.
225/// For example: "fuchsia-boot://domain/#meta/foo.cm"
226///
227/// # Arguments
228/// * `driver_filter` - Filter to the driver that matches the given filter.
229pub async fn get_devices_by_driver(
230    driver_filter: &String,
231    driver_development_proxy: &fdd::ManagerProxy,
232) -> Result<Vec<Device>> {
233    let driver_info = get_driver_by_filter(driver_filter, &driver_development_proxy);
234    let empty: [String; 0] = [];
235    let device_list =
236        get_device_info(&driver_development_proxy, &empty, /* exact_match= */ false);
237
238    let (driver_info, device_list) = futures::join!(driver_info, device_list);
239    let (driver_info, device_list) = (driver_info?, device_list?);
240
241    let mut matches: Vec<Device> = Vec::new();
242    for device_item in device_list.into_iter() {
243        let device: Device = device_item.into();
244        if let (Some(bound_driver_url), Some(url)) = (&device.0.bound_driver_url, &driver_info.url)
245        {
246            if &url == &bound_driver_url {
247                matches.push(device);
248            }
249        }
250    }
251    Ok(matches)
252}