Skip to main content

driver_manager_composite/
composite_node_spec_manager.rs

1// Copyright 2026 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::{CompositeManagerBridge, CompositeNodeSpec};
6use driver_manager_bind::{BindSpecResult, CompositeNodeAndDriver};
7use driver_manager_node::Node;
8use futures::channel::oneshot;
9use log::{error, warn};
10use std::cell::RefCell;
11use std::collections::HashMap;
12use std::rc::Weak;
13use {
14    fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf,
15    fuchsia_async as fasync,
16};
17
18pub struct CompositeNodeSpecManager {
19    bridge: Box<dyn CompositeManagerBridge>,
20    specs: RefCell<HashMap<String, CompositeNodeSpec>>,
21}
22
23impl CompositeNodeSpecManager {
24    pub fn new(bridge: Box<dyn CompositeManagerBridge>) -> Self {
25        Self { bridge, specs: RefCell::new(HashMap::new()) }
26    }
27
28    pub async fn add_spec(
29        &self,
30        fidl_spec: fdf::CompositeNodeSpec,
31        spec: CompositeNodeSpec,
32    ) -> Result<(), fdf::CompositeNodeSpecError> {
33        let name = fidl_spec.name.as_ref().unwrap().clone();
34        if self.specs.borrow().contains_key(&name) {
35            error!("Duplicate composite node spec {}", name);
36            return Err(fdf::CompositeNodeSpecError::AlreadyExists);
37        }
38
39        if let Err(e) = self.bridge.add_spec_to_driver_index(fidl_spec).await {
40            error!("Failed to add composite node spec to driver index: {}", e);
41            return Err(fdf::CompositeNodeSpecError::DriverIndexFailure);
42        }
43
44        self.specs.borrow_mut().insert(name, spec);
45        let bridge = self.bridge.box_clone();
46        fasync::Task::local(async move {
47            bridge.bind_nodes_for_composite_node_spec().await;
48        })
49        .detach();
50        Ok(())
51    }
52
53    pub fn bind_parent_spec(
54        &self,
55        composite_parents: &[fdf::CompositeParent],
56        node_ptr: Weak<Node>,
57        enable_multibind: bool,
58    ) -> Result<BindSpecResult, zx::Status> {
59        if composite_parents.is_empty() {
60            error!("composite_parents needs to contain at least one composite parent.");
61            return Err(zx::Status::INVALID_ARGS);
62        }
63
64        let mut bound_composite_parents = Vec::new();
65        let mut node_and_drivers = Vec::new();
66
67        for composite_parent in composite_parents {
68            let composite = match &composite_parent.composite {
69                Some(c) => c,
70                None => {
71                    warn!("CompositeParent missing composite.");
72                    continue;
73                }
74            };
75
76            let index = match composite_parent.index {
77                Some(i) => i,
78                None => {
79                    warn!("CompositeParent missing index.");
80                    continue;
81                }
82            };
83
84            let matched_driver = match &composite.matched_driver {
85                Some(md) => md,
86                None => continue,
87            };
88
89            if matched_driver.composite_driver.is_none() || matched_driver.parent_names.is_none() {
90                warn!("CompositeDriverMatch does not have all needed fields.");
91                continue;
92            }
93
94            let spec_info = match &composite.spec {
95                Some(s) => s,
96                None => {
97                    warn!("CompositeInfo missing spec.");
98                    continue;
99                }
100            };
101
102            let (name, parents) = match (&spec_info.name, &spec_info.parents, &spec_info.parents2) {
103                (Some(name), Some(parents), None) => (name, parents.len()),
104                (Some(name), None, Some(parents)) => (name, parents.len()),
105                _ => {
106                    warn!("CompositeNodeSpec missing name or parents.");
107                    continue;
108                }
109            };
110
111            if index as usize >= parents
112                || matched_driver.parent_names.as_ref().unwrap().len() != parents
113            {
114                warn!(
115                    "Parent names count does not match the spec parent count or index is out of bounds."
116                );
117                continue;
118            }
119
120            let mut specs = self.specs.borrow_mut();
121            let spec = match specs.get_mut(name) {
122                Some(s) => s,
123                None => {
124                    error!("Missing composite node spec {}", name);
125                    continue;
126                }
127            };
128
129            match spec.bind_parent(composite_parent.clone(), node_ptr.clone()) {
130                Ok(Some(composite_node)) => {
131                    bound_composite_parents.push(composite_parent.clone());
132                    if let Some(driver) = &matched_driver.composite_driver {
133                        node_and_drivers.push(CompositeNodeAndDriver {
134                            driver: driver.clone(),
135                            node: composite_node,
136                        });
137                    }
138                }
139                Ok(None) => {
140                    bound_composite_parents.push(composite_parent.clone());
141                }
142                Err(zx::Status::ALREADY_BOUND) => {
143                    continue;
144                }
145                Err(e) => {
146                    error!("Failed to bind node: {}", e);
147                    continue;
148                }
149            }
150
151            if !enable_multibind {
152                break;
153            }
154        }
155
156        if !bound_composite_parents.is_empty() {
157            Ok(BindSpecResult {
158                bound_composite_parents,
159                completed_node_and_drivers: node_and_drivers,
160            })
161        } else {
162            Err(zx::Status::NOT_FOUND)
163        }
164    }
165
166    pub async fn rebind(
167        &self,
168        spec_name: String,
169        restart_driver_url_suffix: Option<String>,
170    ) -> Result<(), zx::Status> {
171        if !self.specs.borrow().contains_key(&spec_name) {
172            warn!("Spec {} is not available for rebind", spec_name);
173            return Err(zx::Status::NOT_FOUND);
174        }
175
176        self.bridge
177            .request_rebind_from_driver_index(spec_name.clone(), restart_driver_url_suffix)
178            .await?;
179
180        self.on_request_rebind_complete(spec_name).await
181    }
182
183    async fn on_request_rebind_complete(&self, spec_name: String) -> Result<(), zx::Status> {
184        let rx = {
185            let mut specs = self.specs.borrow_mut();
186            let spec = specs.get_mut(&spec_name).unwrap();
187
188            let (tx, rx) = oneshot::channel();
189            spec.remove(tx);
190            rx
191        };
192        rx.await.map_err(|_| zx::Status::INTERNAL)??;
193
194        log::debug!("Rebinding composite node spec {}", spec_name);
195        self.bridge.bind_nodes_for_composite_node_spec().await;
196        Ok(())
197    }
198
199    pub fn get_composite_info(&self) -> Vec<fdd::CompositeNodeInfo> {
200        self.specs.borrow().values().map(|spec| spec.get_composite_info()).collect()
201    }
202}