source: mainline/uspace/srv/devman/fun.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup devman
30 * @{
31 */
32
33#include <errno.h>
34#include <io/log.h>
35#include <loc.h>
36
37#include "dev.h"
38#include "devman.h"
39#include "devtree.h"
40#include "driver.h"
41#include "fun.h"
42#include "main.h"
43#include "loc.h"
44
45static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
46
47/* Function nodes */
48
49/** Create a new function node.
50 *
51 * @return A function node structure.
52 */
53fun_node_t *create_fun_node(void)
54{
55 fun_node_t *fun;
56
57 fun = calloc(1, sizeof(fun_node_t));
58 if (fun == NULL)
59 return NULL;
60
61 fun->state = FUN_INIT;
62 atomic_set(&fun->refcnt, 0);
63 fibril_mutex_initialize(&fun->busy_lock);
64 link_initialize(&fun->dev_functions);
65 list_initialize(&fun->match_ids.ids);
66
67 return fun;
68}
69
70/** Delete a function node.
71 *
72 * @param fun The device node structure.
73 */
74void delete_fun_node(fun_node_t *fun)
75{
76 assert(fun->dev == NULL);
77 assert(fun->child == NULL);
78
79 clean_match_ids(&fun->match_ids);
80 free(fun->name);
81 free(fun->pathname);
82 free(fun);
83}
84
85/** Increase function node reference count.
86 *
87 * @param fun Function node
88 */
89void fun_add_ref(fun_node_t *fun)
90{
91 atomic_inc(&fun->refcnt);
92}
93
94/** Decrease function node reference count.
95 *
96 * When the count drops to zero the function node is freed.
97 *
98 * @param fun Function node
99 */
100void fun_del_ref(fun_node_t *fun)
101{
102 if (atomic_predec(&fun->refcnt) == 0)
103 delete_fun_node(fun);
104}
105
106/** Make function busy for reconfiguration operations. */
107void fun_busy_lock(fun_node_t *fun)
108{
109 fibril_mutex_lock(&fun->busy_lock);
110}
111
112/** Mark end of reconfiguration operation. */
113void fun_busy_unlock(fun_node_t *fun)
114{
115 fibril_mutex_unlock(&fun->busy_lock);
116}
117
118/** Find the function node with the specified handle.
119 *
120 * @param tree The device tree where we look for the device node.
121 * @param handle The handle of the function.
122 * @return The function node.
123 */
124fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
125{
126 fun_node_t *fun;
127
128 assert(fibril_rwlock_is_locked(&tree->rwlock));
129
130 ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
131 if (link == NULL)
132 return NULL;
133
134 fun = hash_table_get_inst(link, fun_node_t, devman_fun);
135
136 return fun;
137}
138
139/** Find the function node with the specified handle.
140 *
141 * @param tree The device tree where we look for the device node.
142 * @param handle The handle of the function.
143 * @return The function node.
144 */
145fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
146{
147 fun_node_t *fun = NULL;
148
149 fibril_rwlock_read_lock(&tree->rwlock);
150
151 fun = find_fun_node_no_lock(tree, handle);
152 if (fun != NULL)
153 fun_add_ref(fun);
154
155 fibril_rwlock_read_unlock(&tree->rwlock);
156
157 return fun;
158}
159
160/** Create and set device's full path in device tree.
161 *
162 * @param tree Device tree
163 * @param node The device's device node.
164 * @param parent The parent device node.
165 * @return True on success, false otherwise (insufficient
166 * resources etc.).
167 */
168bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
169{
170 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
171 assert(fun->name != NULL);
172
173 size_t pathsize = (str_size(fun->name) + 1);
174 if (parent != NULL)
175 pathsize += str_size(parent->pathname) + 1;
176
177 fun->pathname = (char *) malloc(pathsize);
178 if (fun->pathname == NULL) {
179 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
180 return false;
181 }
182
183 if (parent != NULL) {
184 str_cpy(fun->pathname, pathsize, parent->pathname);
185 str_append(fun->pathname, pathsize, "/");
186 str_append(fun->pathname, pathsize, fun->name);
187 } else {
188 str_cpy(fun->pathname, pathsize, fun->name);
189 }
190
191 return true;
192}
193
194/** Find function node with a specified path in the device tree.
195 *
196 * @param path The path of the function node in the device tree.
197 * @param tree The device tree.
198 * @return The function node if it is present in the tree, NULL
199 * otherwise.
200 */
201fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
202{
203 assert(path != NULL);
204
205 bool is_absolute = path[0] == '/';
206 if (!is_absolute) {
207 return NULL;
208 }
209
210 fibril_rwlock_read_lock(&tree->rwlock);
211
212 fun_node_t *fun = tree->root_node;
213 fun_add_ref(fun);
214 /*
215 * Relative path to the function from its parent (but with '/' at the
216 * beginning)
217 */
218 char *rel_path = path;
219 char *next_path_elem = NULL;
220 bool cont = (rel_path[1] != '\0');
221
222 while (cont && fun != NULL) {
223 next_path_elem = get_path_elem_end(rel_path + 1);
224 if (next_path_elem[0] == '/') {
225 cont = true;
226 next_path_elem[0] = 0;
227 } else {
228 cont = false;
229 }
230
231 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
232 fun_del_ref(fun);
233 fun = cfun;
234
235 if (cont) {
236 /* Restore the original path. */
237 next_path_elem[0] = '/';
238 }
239 rel_path = next_path_elem;
240 }
241
242 fibril_rwlock_read_unlock(&tree->rwlock);
243
244 return fun;
245}
246
247/** Find function with a specified name belonging to given device.
248 *
249 * Device tree rwlock should be held at least for reading.
250 *
251 * @param tree Device tree
252 * @param dev Device the function belongs to.
253 * @param name Function name (not path).
254 * @return Function node.
255 * @retval NULL No function with given name.
256 */
257fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
258 const char *name)
259{
260 assert(name != NULL);
261 assert(fibril_rwlock_is_locked(&tree->rwlock));
262
263 list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
264 if (str_cmp(name, fun->name) == 0) {
265 fun_add_ref(fun);
266 return fun;
267 }
268 }
269
270 return NULL;
271}
272
273/** Find child function node with a specified name.
274 *
275 * Device tree rwlock should be held at least for reading.
276 *
277 * @param tree Device tree
278 * @param parent The parent function node.
279 * @param name The name of the child function.
280 * @return The child function node.
281 */
282static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
283 const char *name)
284{
285 return find_fun_node_in_device(tree, pfun->child, name);
286}
287
288static errno_t assign_driver_fibril(void *arg)
289{
290 dev_node_t *dev_node = (dev_node_t *) arg;
291 assign_driver(dev_node, &drivers_list, &device_tree);
292
293 /* Delete one reference we got from the caller. */
294 dev_del_ref(dev_node);
295 return EOK;
296}
297
298errno_t fun_online(fun_node_t *fun)
299{
300 dev_node_t *dev;
301
302 fibril_rwlock_write_lock(&device_tree.rwlock);
303
304 if (fun->state == FUN_ON_LINE) {
305 fibril_rwlock_write_unlock(&device_tree.rwlock);
306 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
307 fun->pathname);
308 return EOK;
309 }
310
311 if (fun->ftype == fun_inner) {
312 dev = create_dev_node();
313 if (dev == NULL) {
314 fibril_rwlock_write_unlock(&device_tree.rwlock);
315 return ENOMEM;
316 }
317
318 insert_dev_node(&device_tree, dev, fun);
319 dev_add_ref(dev);
320 }
321
322 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
323
324 if (fun->ftype == fun_inner) {
325 dev = fun->child;
326 assert(dev != NULL);
327
328 /* Give one reference over to assign_driver_fibril(). */
329 dev_add_ref(dev);
330
331 /*
332 * Try to find a suitable driver and assign it to the device. We do
333 * not want to block the current fibril that is used for processing
334 * incoming calls: we will launch a separate fibril to handle the
335 * driver assigning. That is because assign_driver can actually include
336 * task spawning which could take some time.
337 */
338 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
339 if (assign_fibril == 0) {
340 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
341 "assigning driver.");
342 /* XXX Cleanup */
343 fibril_rwlock_write_unlock(&device_tree.rwlock);
344 return ENOMEM;
345 }
346 fibril_add_ready(assign_fibril);
347 } else
348 loc_register_tree_function(fun, &device_tree);
349
350 fun->state = FUN_ON_LINE;
351 fibril_rwlock_write_unlock(&device_tree.rwlock);
352
353 return EOK;
354}
355
356errno_t fun_offline(fun_node_t *fun)
357{
358 errno_t rc;
359
360 fibril_rwlock_write_lock(&device_tree.rwlock);
361
362 if (fun->state == FUN_OFF_LINE) {
363 fibril_rwlock_write_unlock(&device_tree.rwlock);
364 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
365 fun->pathname);
366 return EOK;
367 }
368
369 if (fun->ftype == fun_inner) {
370 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
371 fun->pathname);
372
373 if (fun->child != NULL) {
374 dev_node_t *dev = fun->child;
375 device_state_t dev_state;
376
377 dev_add_ref(dev);
378 dev_state = dev->state;
379
380 fibril_rwlock_write_unlock(&device_tree.rwlock);
381
382 /* If device is owned by driver, ask driver to give it up. */
383 if (dev_state == DEVICE_USABLE) {
384 rc = driver_dev_remove(&device_tree, dev);
385 if (rc != EOK) {
386 dev_del_ref(dev);
387 return ENOTSUP;
388 }
389 }
390
391 /* Verify that driver removed all functions */
392 fibril_rwlock_read_lock(&device_tree.rwlock);
393 if (!list_empty(&dev->functions)) {
394 fibril_rwlock_read_unlock(&device_tree.rwlock);
395 dev_del_ref(dev);
396 return EIO;
397 }
398
399 driver_t *driver = dev->drv;
400 fibril_rwlock_read_unlock(&device_tree.rwlock);
401
402 if (driver)
403 detach_driver(&device_tree, dev);
404
405 fibril_rwlock_write_lock(&device_tree.rwlock);
406 remove_dev_node(&device_tree, dev);
407
408 /* Delete ref created when node was inserted */
409 dev_del_ref(dev);
410 /* Delete ref created by dev_add_ref(dev) above */
411 dev_del_ref(dev);
412 }
413 } else {
414 /* Unregister from location service */
415 rc = loc_unregister_tree_function(fun, &device_tree);
416 if (rc != EOK) {
417 fibril_rwlock_write_unlock(&device_tree.rwlock);
418 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
419 return EIO;
420 }
421
422 fun->service_id = 0;
423 }
424
425 fun->state = FUN_OFF_LINE;
426 fibril_rwlock_write_unlock(&device_tree.rwlock);
427
428 return EOK;
429}
430
431/** @}
432 */
Note: See TracBrowser for help on using the repository browser.