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::{anyhow, format_err, Context, Result};
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    loop {
54        let mut device_info =
55            iterator.get_next().await.context("FIDL call to get device info failed")?;
56        if device_info.len() == 0 {
57            break;
58        }
59        info_result.append(&mut device_info)
60    }
61    Ok(info_result)
62}
63
64/// Combines pagination results into a single vector.
65pub async fn get_driver_info(
66    service: &fdd::ManagerProxy,
67    driver_filter: &[String],
68) -> Result<Vec<fdf::DriverInfo>> {
69    let (iterator, iterator_server) =
70        fidl::endpoints::create_proxy::<fdd::DriverInfoIteratorMarker>();
71
72    service
73        .get_driver_info(driver_filter, iterator_server)
74        .context("FIDL call to get driver info failed")?;
75
76    let mut info_result = Vec::new();
77    loop {
78        let mut driver_info =
79            iterator.get_next().await.context("FIDL call to get driver info failed")?;
80        if driver_info.len() == 0 {
81            break;
82        }
83        info_result.append(&mut driver_info)
84    }
85    Ok(info_result)
86}
87
88/// Combines pagination results into a single vector.
89pub async fn get_composite_node_specs(
90    service: &fdd::ManagerProxy,
91    name_filter: Option<String>,
92) -> Result<Vec<fdf::CompositeInfo>> {
93    let (iterator, iterator_server) =
94        fidl::endpoints::create_proxy::<fdd::CompositeNodeSpecIteratorMarker>();
95
96    service
97        .get_composite_node_specs(name_filter.as_deref(), iterator_server)
98        .context("FIDL call to get node groups failed")?;
99
100    let mut info_result = Vec::new();
101    loop {
102        let mut node_groups =
103            iterator.get_next().await.context("FIDL call to get node groups failed")?;
104        if node_groups.is_empty() {
105            break;
106        }
107        info_result.append(&mut node_groups)
108    }
109    Ok(info_result)
110}
111
112/// Gets the desired DriverInfo instance.
113///
114/// Filter based on the driver's URL.
115/// For example: "fuchsia-boot://domain/#meta/foo.cm"
116///
117/// # Arguments
118/// * `driver_filter` - Filter to the driver that matches the given filter.
119pub async fn get_driver_by_filter(
120    driver_filter: &String,
121    driver_development_proxy: &fdd::ManagerProxy,
122) -> Result<fdf::DriverInfo> {
123    let filter_list: [String; 1] = [driver_filter.to_string()];
124    let driver_list = get_driver_info(&driver_development_proxy, &filter_list).await?;
125    if driver_list.len() != 1 {
126        return Err(anyhow!(
127            "There should be exactly one match for '{}'. Found {}.",
128            driver_filter,
129            driver_list.len()
130        ));
131    }
132    let mut driver_info: Option<fdf::DriverInfo> = None;
133
134    // Confirm this is the correct match.
135    let driver = &driver_list[0];
136    if let Some(ref url) = driver.url {
137        if url == driver_filter {
138            driver_info = Some(driver.clone());
139        }
140    }
141    match driver_info {
142        Some(driver) => Ok(driver),
143        _ => Err(anyhow!("Did not find matching driver for: {}", driver_filter)),
144    }
145}
146
147/// Gets the driver that is bound to the given device.
148///
149/// Is able to fuzzy match on the device's topological path, where the shortest match
150/// will be the one chosen.
151///
152/// # Arguments
153/// * `device_topo_path` - The device's topological path. e.g. sys/platform/.../device
154pub async fn get_driver_by_device(
155    device_topo_path: &String,
156    driver_development_proxy: &fdd::ManagerProxy,
157) -> Result<fdf::DriverInfo> {
158    let device_filter: [String; 1] = [device_topo_path.to_string()];
159    let mut device_list =
160        get_device_info(&driver_development_proxy, &device_filter, /* exact_match= */ true).await?;
161    if device_list.len() != 1 {
162        let fuzzy_device_list = get_device_info(
163            &driver_development_proxy,
164            &device_filter,
165            /* exact_match= */ false,
166        )
167        .await?;
168        if fuzzy_device_list.len() == 0 {
169            return Err(anyhow!("No devices matched the query: {}", device_topo_path.to_string()));
170        } else if fuzzy_device_list.len() > 1 {
171            let mut builder = "Found multiple matches. Did you mean one of these?\n\n".to_string();
172            for item in fuzzy_device_list {
173                let device: Device = item.into();
174                // We don't appear to have a string builder crate in-tree.
175                builder = format!("{}{}\n", builder, device.get_full_name()?);
176            }
177            return Err(anyhow!(builder));
178        }
179        device_list = fuzzy_device_list;
180    }
181
182    let found_device = device_list.remove(0);
183    match found_device.bound_driver_url {
184        Some(ref driver_filter) => {
185            get_driver_by_filter(&driver_filter, &driver_development_proxy).await
186        }
187        _ => Err(anyhow!("Did not find driver for device {}", &device_topo_path)),
188    }
189}
190
191/// Gets the devices that are bound to the given driver.
192///
193/// Filter based on the driver's URL.
194/// For example: "fuchsia-boot://domain/#meta/foo.cm"
195///
196/// # Arguments
197/// * `driver_filter` - Filter to the driver that matches the given filter.
198pub async fn get_devices_by_driver(
199    driver_filter: &String,
200    driver_development_proxy: &fdd::ManagerProxy,
201) -> Result<Vec<Device>> {
202    let driver_info = get_driver_by_filter(driver_filter, &driver_development_proxy);
203    let empty: [String; 0] = [];
204    let device_list =
205        get_device_info(&driver_development_proxy, &empty, /* exact_match= */ false);
206
207    let (driver_info, device_list) = futures::join!(driver_info, device_list);
208    let (driver_info, device_list) = (driver_info?, device_list?);
209
210    let mut matches: Vec<Device> = Vec::new();
211    for device_item in device_list.into_iter() {
212        let device: Device = device_item.into();
213        if let (Some(bound_driver_url), Some(url)) = (&device.0.bound_driver_url, &driver_info.url)
214        {
215            if &url == &bound_driver_url {
216                matches.push(device);
217            }
218        }
219    }
220    Ok(matches)
221}