source: mainline/uspace/srv/devman/fun.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 10.7 KB
RevLine 
[38e52c92]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>
[02e5e34]35#include <loc.h>
[38e52c92]36
[02e5e34]37#include "dev.h"
[38e52c92]38#include "devman.h"
[02e5e34]39#include "devtree.h"
40#include "driver.h"
[38e52c92]41#include "fun.h"
[02e5e34]42#include "main.h"
43#include "loc.h"
[38e52c92]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;
[a35b458]60
[38e52c92]61 fun->state = FUN_INIT;
[498ced1]62 refcount_init(&fun->refcnt);
[38e52c92]63 fibril_mutex_initialize(&fun->busy_lock);
64 link_initialize(&fun->dev_functions);
65 list_initialize(&fun->match_ids.ids);
[a35b458]66
[38e52c92]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);
[a35b458]78
[38e52c92]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{
[498ced1]91 refcount_up(&fun->refcnt);
[38e52c92]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{
[498ced1]102 if (refcount_down(&fun->refcnt))
[38e52c92]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;
[a35b458]127
[38e52c92]128 assert(fibril_rwlock_is_locked(&tree->rwlock));
[a35b458]129
[38e52c92]130 ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
131 if (link == NULL)
132 return NULL;
[a35b458]133
[38e52c92]134 fun = hash_table_get_inst(link, fun_node_t, devman_fun);
[a35b458]135
[38e52c92]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;
[a35b458]148
[38e52c92]149 fibril_rwlock_read_lock(&tree->rwlock);
[a35b458]150
[38e52c92]151 fun = find_fun_node_no_lock(tree, handle);
152 if (fun != NULL)
153 fun_add_ref(fun);
[a35b458]154
[38e52c92]155 fibril_rwlock_read_unlock(&tree->rwlock);
[a35b458]156
[38e52c92]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);
[a35b458]172
[08e103d4]173 size_t pathsize = (str_bytes(fun->name) + 1);
[38e52c92]174 if (parent != NULL)
[08e103d4]175 pathsize += str_bytes(parent->pathname) + 1;
[a35b458]176
[38e52c92]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 }
[a35b458]182
[38e52c92]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 }
[a35b458]190
[38e52c92]191 return true;
192}
193
194/** Find function node with a specified path in the device tree.
[1b20da0]195 *
[38e52c92]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);
[a35b458]211
[38e52c92]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');
[a35b458]221
[38e52c92]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 }
[a35b458]230
[38e52c92]231 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
232 fun_del_ref(fun);
233 fun = cfun;
[a35b458]234
[38e52c92]235 if (cont) {
236 /* Restore the original path. */
237 next_path_elem[0] = '/';
238 }
239 rel_path = next_path_elem;
240 }
[a35b458]241
[38e52c92]242 fibril_rwlock_read_unlock(&tree->rwlock);
[a35b458]243
[38e52c92]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
[08bc23d]263 list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
[38e52c92]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
[b7fd2a0]288static errno_t assign_driver_fibril(void *arg)
[02e5e34]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
[b7fd2a0]298errno_t fun_online(fun_node_t *fun)
[02e5e34]299{
300 dev_node_t *dev;
[a35b458]301
[02e5e34]302 fibril_rwlock_write_lock(&device_tree.rwlock);
[a35b458]303
[02e5e34]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 }
[a35b458]310
[02e5e34]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 }
[a35b458]317
[02e5e34]318 insert_dev_node(&device_tree, dev, fun);
319 }
[a35b458]320
[02e5e34]321 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
[a35b458]322
[02e5e34]323 if (fun->ftype == fun_inner) {
324 dev = fun->child;
325 assert(dev != NULL);
[a35b458]326
[02e5e34]327 /* Give one reference over to assign_driver_fibril(). */
328 dev_add_ref(dev);
[a35b458]329
[02e5e34]330 /*
331 * Try to find a suitable driver and assign it to the device. We do
332 * not want to block the current fibril that is used for processing
333 * incoming calls: we will launch a separate fibril to handle the
334 * driver assigning. That is because assign_driver can actually include
335 * task spawning which could take some time.
336 */
337 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
338 if (assign_fibril == 0) {
339 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
340 "assigning driver.");
341 /* XXX Cleanup */
342 fibril_rwlock_write_unlock(&device_tree.rwlock);
343 return ENOMEM;
344 }
345 fibril_add_ready(assign_fibril);
346 } else
347 loc_register_tree_function(fun, &device_tree);
[a35b458]348
[ba0eac5]349 fun->state = FUN_ON_LINE;
[02e5e34]350 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]351
[02e5e34]352 return EOK;
353}
354
[b7fd2a0]355errno_t fun_offline(fun_node_t *fun)
[02e5e34]356{
[b7fd2a0]357 errno_t rc;
[a35b458]358
[02e5e34]359 fibril_rwlock_write_lock(&device_tree.rwlock);
[a35b458]360
[02e5e34]361 if (fun->state == FUN_OFF_LINE) {
362 fibril_rwlock_write_unlock(&device_tree.rwlock);
363 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
364 fun->pathname);
365 return EOK;
366 }
[a35b458]367
[02e5e34]368 if (fun->ftype == fun_inner) {
369 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
370 fun->pathname);
[a35b458]371
[02e5e34]372 if (fun->child != NULL) {
373 dev_node_t *dev = fun->child;
374 device_state_t dev_state;
[a35b458]375
[02e5e34]376 dev_add_ref(dev);
377 dev_state = dev->state;
[a35b458]378
[02e5e34]379 fibril_rwlock_write_unlock(&device_tree.rwlock);
380
381 /* If device is owned by driver, ask driver to give it up. */
382 if (dev_state == DEVICE_USABLE) {
383 rc = driver_dev_remove(&device_tree, dev);
384 if (rc != EOK) {
385 dev_del_ref(dev);
386 return ENOTSUP;
387 }
388 }
[a35b458]389
[02e5e34]390 /* Verify that driver removed all functions */
391 fibril_rwlock_read_lock(&device_tree.rwlock);
392 if (!list_empty(&dev->functions)) {
393 fibril_rwlock_read_unlock(&device_tree.rwlock);
394 dev_del_ref(dev);
395 return EIO;
396 }
[a35b458]397
[02e5e34]398 driver_t *driver = dev->drv;
399 fibril_rwlock_read_unlock(&device_tree.rwlock);
[a35b458]400
[02e5e34]401 if (driver)
402 detach_driver(&device_tree, dev);
[a35b458]403
[02e5e34]404 fibril_rwlock_write_lock(&device_tree.rwlock);
405 remove_dev_node(&device_tree, dev);
[a35b458]406
[02e5e34]407 /* Delete ref created when node was inserted */
408 dev_del_ref(dev);
409 /* Delete ref created by dev_add_ref(dev) above */
410 dev_del_ref(dev);
411 }
412 } else {
413 /* Unregister from location service */
[96ef672]414 rc = loc_unregister_tree_function(fun, &device_tree);
[02e5e34]415 if (rc != EOK) {
416 fibril_rwlock_write_unlock(&device_tree.rwlock);
417 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
418 return EIO;
419 }
[a35b458]420
[02e5e34]421 fun->service_id = 0;
422 }
[a35b458]423
[02e5e34]424 fun->state = FUN_OFF_LINE;
425 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]426
[02e5e34]427 return EOK;
428}
429
[38e52c92]430/** @}
431 */
Note: See TracBrowser for help on using the repository browser.