_driver_rustc/
lib.rs

1// Copyright 2025 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 crate::graph::{NodeGraph, NodeId, Path, PathId};
6use fdf_component::{
7    driver_register, Driver, DriverContext, Node, NodeBuilder, ZirconServiceOffer,
8};
9use fidl::endpoints::ClientEnd;
10use fidl_fuchsia_driver_framework::NodeControllerMarker;
11use fidl_fuchsia_hardware_interconnect as icc;
12use fuchsia_component::server::ServiceFs;
13use fuchsia_sync::Mutex;
14use futures::{StreamExt, TryStreamExt};
15use log::{error, warn};
16use std::collections::BTreeMap;
17use std::sync::Arc;
18use zx::Status;
19
20mod graph;
21
22driver_register!(InterconnectDriver);
23
24struct Child {
25    /// List of nodes following directed path from start of path to end of path.
26    path: Path,
27    /// Directed graph which stores all nodes and bandwidth requests for each of their incoming
28    /// edges.
29    graph: Arc<Mutex<NodeGraph>>,
30    #[allow(unused)]
31    controller: ClientEnd<NodeControllerMarker>,
32    device: icc::DeviceProxy,
33}
34
35impl Child {
36    async fn set_bandwidth(
37        &self,
38        average_bandwidth_bps: Option<u64>,
39        peak_bandwidth_bps: Option<u64>,
40    ) -> Result<(), Status> {
41        let average_bandwidth_bps = average_bandwidth_bps.ok_or(Status::INVALID_ARGS)?;
42        let peak_bandwidth_bps = peak_bandwidth_bps.ok_or(Status::INVALID_ARGS)?;
43
44        let requests = {
45            let mut graph = self.graph.lock();
46            graph.update_path(&self.path, average_bandwidth_bps, peak_bandwidth_bps);
47            graph.make_bandwidth_requests(&self.path)
48        };
49
50        self.device
51            .set_nodes_bandwidth(&requests)
52            .await
53            .map_err(|err| {
54                error!("Failed to set bandwidth with {err}");
55                Status::INTERNAL
56            })?
57            .map_err(Status::from_raw)?;
58
59        // TODO(b/405206028): On failure, try to set old values?
60
61        Ok(())
62    }
63
64    async fn run_path_server(&self, mut service: icc::PathRequestStream) {
65        use icc::PathRequest::*;
66        while let Some(req) = service.try_next().await.unwrap() {
67            match req {
68                SetBandwidth { payload, responder, .. } => responder.send(
69                    self.set_bandwidth(payload.average_bandwidth_bps, payload.peak_bandwidth_bps)
70                        .await
71                        .map_err(Status::into_raw),
72                ),
73                // Ignore unknown requests.
74                _ => {
75                    warn!("Received unknown path request");
76                    Ok(())
77                }
78            }
79            .unwrap();
80        }
81    }
82}
83
84#[allow(unused)]
85struct InterconnectDriver {
86    node: Node,
87    children: Arc<BTreeMap<String, Child>>,
88    scope: fuchsia_async::Scope,
89}
90
91impl Driver for InterconnectDriver {
92    const NAME: &str = "interconnect";
93
94    async fn start(mut context: DriverContext) -> Result<Self, Status> {
95        let node = context.take_node()?;
96
97        let device = context
98            .incoming
99            .service_marker(icc::ServiceMarker)
100            .connect()?
101            .connect_to_device()
102            .map_err(|err| {
103                error!("Error connecting to interconnect device at driver startup: {err}");
104                Status::INTERNAL
105            })?;
106
107        let (nodes, edges) = device.get_node_graph().await.map_err(|err| {
108            error!("Failed to get node graph with {err}");
109            Status::INTERNAL
110        })?;
111        let mut graph = NodeGraph::new(nodes, edges)?;
112
113        let path_endpoints = device.get_path_endpoints().await.map_err(|err| {
114            error!("Failed to get path endpoints with {err}");
115            Status::INTERNAL
116        })?;
117        let paths: Vec<_> = Result::from_iter(path_endpoints.into_iter().map(|path| {
118            let path_id = PathId(path.id.ok_or(Status::INVALID_ARGS)?);
119            let path_name = path.name.ok_or(Status::INVALID_ARGS)?;
120            let src_node_id = NodeId(path.src_node_id.ok_or(Status::INVALID_ARGS)?);
121            let dst_node_id = NodeId(path.dst_node_id.ok_or(Status::INVALID_ARGS)?);
122            Ok::<_, Status>(graph.make_path(path_id, path_name, src_node_id, dst_node_id)?)
123        }))?;
124
125        let mut outgoing = ServiceFs::new();
126
127        let graph = Arc::new(Mutex::new(graph));
128        let mut children = BTreeMap::new();
129        for path in paths {
130            let name = format!("{}-{}", path.name(), path.id());
131            let name_clone = name.clone();
132            let offer = ZirconServiceOffer::new()
133                .add_default_named(&mut outgoing, &name, move |req| {
134                    let icc::PathServiceRequest::Path(service) = req;
135                    (service, name_clone.clone())
136                })
137                .build();
138
139            let node_args = NodeBuilder::new(&name)
140                .add_property(bind_fuchsia::BIND_INTERCONNECT_PATH_ID, path.id().0)
141                .add_offer(offer)
142                .build();
143            let controller = node.add_child(node_args).await?;
144            let graph = graph.clone();
145            let device = device.clone();
146            children.insert(name.clone(), Child { path, graph, controller, device });
147        }
148        // TODO(b/405206028): Initialize all nodes to initial bus bandwidths.
149
150        context.serve_outgoing(&mut outgoing)?;
151
152        let children = Arc::new(children);
153
154        let scope = fuchsia_async::Scope::new_with_name("outgoing_directory");
155        let children_clone = children.clone();
156        scope.spawn_local(async move {
157            outgoing
158                .for_each_concurrent(None, move |(request, child_name)| {
159                    let children = children_clone.clone();
160                    async move {
161                        if let Some(node) = children.get(&child_name) {
162                            node.run_path_server(request).await;
163                        } else {
164                            error!("Failed to find child {child_name}");
165                        }
166                    }
167                })
168                .await;
169        });
170
171        Ok(Self { node, children, scope })
172    }
173
174    async fn stop(&self) {}
175}