source: mainline/uspace/srv/devman/devtree.c@ c3d9aaf5

Last change on this file since c3d9aaf5 was c3d9aaf5, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Determine when device (sub)tree is stable.

Devman will only return value when the entire device tree is stable.

  • Property mode set to 100644
File size: 8.8 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2010 Lenka Trochtova
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup devman
31 * @{
32 */
33
34#include <errno.h>
35#include <io/log.h>
36
37#include "dev.h"
38#include "devtree.h"
39#include "devman.h"
40#include "driver.h"
41#include "fun.h"
42
43/* hash table operations */
44
45static inline size_t handle_key_hash(const void *key)
46{
47 const devman_handle_t *handle = key;
48 return *handle;
49}
50
51static size_t devman_devices_hash(const ht_link_t *item)
52{
53 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
54 return handle_key_hash(&dev->handle);
55}
56
57static size_t devman_functions_hash(const ht_link_t *item)
58{
59 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
60 return handle_key_hash(&fun->handle);
61}
62
63static bool devman_devices_key_equal(const void *key, const ht_link_t *item)
64{
65 const devman_handle_t *handle = key;
66 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
67 return dev->handle == *handle;
68}
69
70static bool devman_functions_key_equal(const void *key, const ht_link_t *item)
71{
72 const devman_handle_t *handle = key;
73 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
74 return fun->handle == *handle;
75}
76
77static inline size_t service_id_key_hash(const void *key)
78{
79 const service_id_t *service_id = key;
80 return *service_id;
81}
82
83static size_t loc_functions_hash(const ht_link_t *item)
84{
85 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
86 return service_id_key_hash(&fun->service_id);
87}
88
89static bool loc_functions_key_equal(const void *key, const ht_link_t *item)
90{
91 const service_id_t *service_id = key;
92 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
93 return fun->service_id == *service_id;
94}
95
96static const hash_table_ops_t devman_devices_ops = {
97 .hash = devman_devices_hash,
98 .key_hash = handle_key_hash,
99 .key_equal = devman_devices_key_equal,
100 .equal = NULL,
101 .remove_callback = NULL
102};
103
104static const hash_table_ops_t devman_functions_ops = {
105 .hash = devman_functions_hash,
106 .key_hash = handle_key_hash,
107 .key_equal = devman_functions_key_equal,
108 .equal = NULL,
109 .remove_callback = NULL
110};
111
112static const hash_table_ops_t loc_devices_ops = {
113 .hash = loc_functions_hash,
114 .key_hash = service_id_key_hash,
115 .key_equal = loc_functions_key_equal,
116 .equal = NULL,
117 .remove_callback = NULL
118};
119
120/** Create root device and function node in the device tree.
121 *
122 * @param tree The device tree.
123 * @return True on success, false otherwise.
124 */
125bool create_root_nodes(dev_tree_t *tree)
126{
127 fun_node_t *fun;
128 dev_node_t *dev;
129
130 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
131
132 fibril_rwlock_write_lock(&tree->rwlock);
133
134 /*
135 * Create root function. This is a pseudo function to which
136 * the root device node is attached. It allows us to match
137 * the root device driver in a standard manner, i.e. against
138 * the parent function.
139 */
140
141 fun = create_fun_node();
142 if (fun == NULL) {
143 fibril_rwlock_write_unlock(&tree->rwlock);
144 return false;
145 }
146
147 if (!insert_fun_node(tree, fun, str_dup(""), NULL)) {
148 fun_del_ref(fun); /* fun is destroyed */
149 fibril_rwlock_write_unlock(&tree->rwlock);
150 return false;
151 }
152
153 match_id_t *id = create_match_id();
154 id->id = str_dup("root");
155 id->score = 100;
156 add_match_id(&fun->match_ids, id);
157 tree->root_node = fun;
158
159 /*
160 * Create root device node.
161 */
162 dev = create_dev_node();
163 if (dev == NULL) {
164 fibril_rwlock_write_unlock(&tree->rwlock);
165 return false;
166 }
167
168 insert_dev_node(tree, dev, fun);
169
170 fibril_rwlock_write_unlock(&tree->rwlock);
171
172 return dev != NULL;
173}
174
175/** Initialize the device tree.
176 *
177 * Create root device node of the tree and assign driver to it.
178 *
179 * @param tree The device tree.
180 * @param drivers_list the list of available drivers.
181 * @return True on success, false otherwise.
182 */
183bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
184{
185 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
186
187 tree->current_handle = 0;
188
189 hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
190 hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
191 hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
192
193 fibril_rwlock_initialize(&tree->rwlock);
194
195 /* Create root function and root device and add them to the device tree. */
196 if (!create_root_nodes(tree))
197 return false;
198
199 /* Find suitable driver and start it. */
200 dev_node_t *rdev = tree->root_node->child;
201 dev_add_ref(rdev);
202 bool rc = assign_driver(rdev, drivers_list, tree);
203 dev_del_ref(rdev);
204
205 return rc;
206}
207
208/** Insert new device into device tree.
209 *
210 * @param tree The device tree.
211 * @param dev The newly added device node.
212 * @param pfun The parent function node.
213 *
214 * @return True on success, false otherwise (insufficient resources
215 * etc.).
216 */
217bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
218{
219 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
220
221 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
222 dev, pfun, pfun->pathname);
223
224 /* Add the node to the handle-to-node map. */
225 dev->handle = ++tree->current_handle;
226 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
227
228 /* Add the node to the list of its parent's children. */
229 dev->pfun = pfun;
230 pfun->child = dev;
231
232 return true;
233}
234
235/** Remove device from device tree.
236 *
237 * @param tree Device tree
238 * @param dev Device node
239 */
240void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
241{
242 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
243
244 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
245
246 /* Remove node from the handle-to-node map. */
247 hash_table_remove(&tree->devman_devices, &dev->handle);
248
249 /* Unlink from parent function. */
250 dev->pfun->child = NULL;
251 dev->pfun = NULL;
252
253 dev->state = DEVICE_REMOVED;
254}
255
256/** Insert new function into device tree.
257 *
258 * @param tree The device tree.
259 * @param fun The newly added function node.
260 * @param fun_name The name of the newly added function.
261 * @param dev Owning device node.
262 *
263 * @return True on success, false otherwise (insufficient resources
264 * etc.).
265 */
266bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
267 dev_node_t *dev)
268{
269 fun_node_t *pfun;
270
271 assert(fun_name != NULL);
272 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
273
274 /*
275 * The root function is a special case, it does not belong to any
276 * device so for the root function dev == NULL.
277 */
278 pfun = (dev != NULL) ? dev->pfun : NULL;
279
280 fun->name = fun_name;
281 if (!set_fun_path(tree, fun, pfun)) {
282 return false;
283 }
284
285 /* Add the node to the handle-to-node map. */
286 fun->handle = ++tree->current_handle;
287 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
288
289 /* Add the node to the list of its parent's children. */
290 fun->dev = dev;
291 if (dev != NULL)
292 list_append(&fun->dev_functions, &dev->functions);
293
294 return true;
295}
296
297/** Remove function from device tree.
298 *
299 * @param tree Device tree
300 * @param node Function node to remove
301 */
302void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
303{
304 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
305
306 /* Remove the node from the handle-to-node map. */
307 hash_table_remove(&tree->devman_functions, &fun->handle);
308
309 /* Remove the node from the list of its parent's children. */
310 if (fun->dev != NULL)
311 list_remove(&fun->dev_functions);
312
313 fun->dev = NULL;
314 fun->state = FUN_REMOVED;
315}
316
317/** Wait for device tree to stabilize.
318 *
319 * Blocks until the entire device tree had a chance to finish attaching
320 * all devices.
321 *
322 * @param tree Device tree
323 */
324void dev_tree_wait_stable(dev_tree_t *tree)
325{
326 dev_wait_stable(tree->root_node->child);
327}
328
329/** @}
330 */
Note: See TracBrowser for help on using the repository browser.