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