source: mainline/uspace/srv/devman/fun.c@ 1c635d6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1c635d6 was 02e5e34, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Fun on-/off-line needs to go to fun.c

  • 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 int 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
298int 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 fibril_rwlock_write_unlock(&device_tree.rwlock);
351
352 return EOK;
353}
354
355int fun_offline(fun_node_t *fun)
356{
357 int rc;
358
359 fibril_rwlock_write_lock(&device_tree.rwlock);
360
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 }
367
368 if (fun->ftype == fun_inner) {
369 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
370 fun->pathname);
371
372 if (fun->child != NULL) {
373 dev_node_t *dev = fun->child;
374 device_state_t dev_state;
375
376 dev_add_ref(dev);
377 dev_state = dev->state;
378
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 }
389
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 }
397
398 driver_t *driver = dev->drv;
399 fibril_rwlock_read_unlock(&device_tree.rwlock);
400
401 if (driver)
402 detach_driver(&device_tree, dev);
403
404 fibril_rwlock_write_lock(&device_tree.rwlock);
405 remove_dev_node(&device_tree, dev);
406
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 */
414 rc = loc_service_unregister(fun->service_id);
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 }
420
421 fun->service_id = 0;
422 }
423
424 fun->state = FUN_OFF_LINE;
425 fibril_rwlock_write_unlock(&device_tree.rwlock);
426
427 return EOK;
428}
429
430/** @}
431 */
Note: See TracBrowser for help on using the repository browser.