source: mainline/uspace/srv/devman/driver.c@ acb8766e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since acb8766e was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 19.0 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 <dirent.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <sys/stat.h>
37#include <io/log.h>
38#include <ipc/driver.h>
39#include <loc.h>
40#include <str_error.h>
41#include <stdio.h>
42#include <task.h>
43
44#include "dev.h"
45#include "devman.h"
46#include "driver.h"
47#include "match.h"
48
49/**
50 * Initialize the list of device driver's.
51 *
52 * @param drv_list the list of device driver's.
53 *
54 */
55void init_driver_list(driver_list_t *drv_list)
56{
57 assert(drv_list != NULL);
58
59 list_initialize(&drv_list->drivers);
60 fibril_mutex_initialize(&drv_list->drivers_mutex);
61 drv_list->next_handle = 1;
62}
63
64/** Allocate and initialize a new driver structure.
65 *
66 * @return Driver structure.
67 */
68driver_t *create_driver(void)
69{
70 driver_t *res = malloc(sizeof(driver_t));
71 if (res != NULL)
72 init_driver(res);
73 return res;
74}
75
76/** Add a driver to the list of drivers.
77 *
78 * @param drivers_list List of drivers.
79 * @param drv Driver structure.
80 */
81void add_driver(driver_list_t *drivers_list, driver_t *drv)
82{
83 fibril_mutex_lock(&drivers_list->drivers_mutex);
84 list_append(&drv->drivers, &drivers_list->drivers);
85 drv->handle = drivers_list->next_handle++;
86 fibril_mutex_unlock(&drivers_list->drivers_mutex);
87
88 log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' was added to the list of available "
89 "drivers.", drv->name);
90}
91
92/**
93 * Get information about a driver.
94 *
95 * Each driver has its own directory in the base directory.
96 * The name of the driver's directory is the same as the name of the driver.
97 * The driver's directory contains driver's binary (named as the driver without
98 * extension) and the configuration file with match ids for device-to-driver
99 * matching (named as the driver with a special extension).
100 *
101 * This function searches for the driver's directory and containing
102 * configuration files. If all the files needed are found, they are parsed and
103 * the information about the driver is stored in the driver's structure.
104 *
105 * @param base_path The base directory, in which we look for driver's
106 * subdirectory.
107 * @param name The name of the driver.
108 * @param drv The driver structure to fill information in.
109 *
110 * @return True on success, false otherwise.
111 */
112bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
113{
114 log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
115 base_path, name);
116
117 assert(base_path != NULL && name != NULL && drv != NULL);
118
119 bool suc = false;
120 char *match_path = NULL;
121 size_t name_size = 0;
122
123 /* Read the list of match ids from the driver's configuration file. */
124 match_path = get_abs_path(base_path, name, MATCH_EXT);
125 if (match_path == NULL)
126 goto cleanup;
127
128 if (!read_match_ids(match_path, &drv->match_ids))
129 goto cleanup;
130
131 /* Allocate and fill driver's name. */
132 name_size = str_size(name) + 1;
133 drv->name = malloc(name_size);
134 if (drv->name == NULL)
135 goto cleanup;
136 str_cpy(drv->name, name_size, name);
137
138 /* Initialize path with driver's binary. */
139 drv->binary_path = get_abs_path(base_path, name, "");
140 if (drv->binary_path == NULL)
141 goto cleanup;
142
143 /* Check whether the driver's binary exists. */
144 struct stat s;
145 if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
146 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
147 drv->binary_path);
148 goto cleanup;
149 }
150
151 suc = true;
152
153cleanup:
154 if (!suc) {
155 free(drv->binary_path);
156 free(drv->name);
157 /* Set the driver structure to the default state. */
158 init_driver(drv);
159 }
160
161 free(match_path);
162
163 return suc;
164}
165
166/** Lookup drivers in the directory.
167 *
168 * @param drivers_list The list of available drivers.
169 * @param dir_path The path to the directory where we search for drivers.
170 * @return Number of drivers which were found.
171 */
172int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
173{
174 log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
175
176 int drv_cnt = 0;
177 DIR *dir = NULL;
178 struct dirent *diren;
179
180 dir = opendir(dir_path);
181
182 if (dir != NULL) {
183 driver_t *drv = create_driver();
184 while ((diren = readdir(dir))) {
185 if (get_driver_info(dir_path, diren->d_name, drv)) {
186 add_driver(drivers_list, drv);
187 drv_cnt++;
188 drv = create_driver();
189 }
190 }
191 delete_driver(drv);
192 closedir(dir);
193 }
194
195 return drv_cnt;
196}
197
198/** Lookup the best matching driver for the specified device in the list of
199 * drivers.
200 *
201 * A match between a device and a driver is found if one of the driver's match
202 * ids match one of the device's match ids. The score of the match is the
203 * product of the driver's and device's score associated with the matching id.
204 * The best matching driver for a device is the driver with the highest score
205 * of the match between the device and the driver.
206 *
207 * @param drivers_list The list of drivers, where we look for the driver
208 * suitable for handling the device.
209 * @param node The device node structure of the device.
210 * @return The best matching driver or NULL if no matching driver
211 * is found.
212 */
213driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
214{
215 driver_t *best_drv = NULL;
216 int best_score = 0, score = 0;
217
218 fibril_mutex_lock(&drivers_list->drivers_mutex);
219
220 list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
221 score = get_match_score(drv, node);
222 if (score > best_score) {
223 best_score = score;
224 best_drv = drv;
225 }
226 }
227
228 fibril_mutex_unlock(&drivers_list->drivers_mutex);
229
230 return best_drv;
231}
232
233/** Assign a driver to a device.
234 *
235 * @param tree Device tree
236 * @param node The device's node in the device tree.
237 * @param drv The driver.
238 */
239void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
240{
241 log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
242 dev->pfun->pathname, drv->name);
243
244 fibril_mutex_lock(&drv->driver_mutex);
245 fibril_rwlock_write_lock(&tree->rwlock);
246
247 dev->drv = drv;
248 list_append(&dev->driver_devices, &drv->devices);
249
250 fibril_rwlock_write_unlock(&tree->rwlock);
251 fibril_mutex_unlock(&drv->driver_mutex);
252}
253
254/** Detach driver from device.
255 *
256 * @param tree Device tree
257 * @param node The device's node in the device tree.
258 * @param drv The driver.
259 */
260void detach_driver(dev_tree_t *tree, dev_node_t *dev)
261{
262 driver_t *drv = dev->drv;
263
264 assert(drv != NULL);
265
266 log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
267 dev->pfun->pathname, drv->name);
268
269 fibril_mutex_lock(&drv->driver_mutex);
270 fibril_rwlock_write_lock(&tree->rwlock);
271
272 dev->drv = NULL;
273 list_remove(&dev->driver_devices);
274
275 fibril_rwlock_write_unlock(&tree->rwlock);
276 fibril_mutex_unlock(&drv->driver_mutex);
277}
278
279/** Start a driver
280 *
281 * @param drv The driver's structure.
282 * @return True if the driver's task is successfully spawned, false
283 * otherwise.
284 */
285bool start_driver(driver_t *drv)
286{
287 int rc;
288
289 assert(fibril_mutex_is_locked(&drv->driver_mutex));
290
291 log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
292
293 rc = task_spawnl(NULL, NULL, drv->binary_path, drv->binary_path, NULL);
294 if (rc != EOK) {
295 log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
296 drv->name, drv->binary_path, str_error(rc));
297 return false;
298 }
299
300 drv->state = DRIVER_STARTING;
301 return true;
302}
303
304/** Find device driver by handle.
305 *
306 * @param drv_list The list of device drivers
307 * @param handle Driver handle
308 * @return The device driver, if it is in the list,
309 * NULL otherwise.
310 */
311driver_t *driver_find(driver_list_t *drv_list, devman_handle_t handle)
312{
313 driver_t *res = NULL;
314
315 fibril_mutex_lock(&drv_list->drivers_mutex);
316
317 list_foreach(drv_list->drivers, drivers, driver_t, drv) {
318 if (drv->handle == handle) {
319 res = drv;
320 break;
321 }
322 }
323
324 fibril_mutex_unlock(&drv_list->drivers_mutex);
325
326 return res;
327}
328
329
330/** Find device driver by name.
331 *
332 * @param drv_list The list of device drivers.
333 * @param drv_name The name of the device driver which is searched.
334 * @return The device driver of the specified name, if it is in the
335 * list, NULL otherwise.
336 */
337driver_t *driver_find_by_name(driver_list_t *drv_list, const char *drv_name)
338{
339 driver_t *res = NULL;
340
341 fibril_mutex_lock(&drv_list->drivers_mutex);
342
343 list_foreach(drv_list->drivers, drivers, driver_t, drv) {
344 if (str_cmp(drv->name, drv_name) == 0) {
345 res = drv;
346 break;
347 }
348 }
349
350 fibril_mutex_unlock(&drv_list->drivers_mutex);
351
352 return res;
353}
354
355/** Notify driver about the devices to which it was assigned.
356 *
357 * @param driver The driver to which the devices are passed.
358 */
359static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
360{
361 dev_node_t *dev;
362 link_t *link;
363
364 log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
365 driver->name);
366
367 fibril_mutex_lock(&driver->driver_mutex);
368
369 /*
370 * Go through devices list as long as there is some device
371 * that has not been passed to the driver.
372 */
373 link = driver->devices.head.next;
374 while (link != &driver->devices.head) {
375 dev = list_get_instance(link, dev_node_t, driver_devices);
376 fibril_rwlock_write_lock(&tree->rwlock);
377
378 if (dev->passed_to_driver) {
379 fibril_rwlock_write_unlock(&tree->rwlock);
380 link = link->next;
381 continue;
382 }
383
384 log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
385 (int)atomic_get(&dev->refcnt));
386 dev_add_ref(dev);
387
388 /*
389 * Unlock to avoid deadlock when adding device
390 * handled by itself.
391 */
392 fibril_mutex_unlock(&driver->driver_mutex);
393 fibril_rwlock_write_unlock(&tree->rwlock);
394
395 add_device(driver, dev, tree);
396
397 dev_del_ref(dev);
398
399 /*
400 * Lock again as we will work with driver's
401 * structure.
402 */
403 fibril_mutex_lock(&driver->driver_mutex);
404
405 /*
406 * Restart the cycle to go through all devices again.
407 */
408 link = driver->devices.head.next;
409 }
410
411 /*
412 * Once we passed all devices to the driver, we need to mark the
413 * driver as running.
414 * It is vital to do it here and inside critical section.
415 *
416 * If we would change the state earlier, other devices added to
417 * the driver would be added to the device list and started
418 * immediately and possibly started here as well.
419 */
420 log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
421 driver->state = DRIVER_RUNNING;
422
423 fibril_mutex_unlock(&driver->driver_mutex);
424}
425
426/** Finish the initialization of a driver after it has succesfully started
427 * and after it has registered itself by the device manager.
428 *
429 * Pass devices formerly matched to the driver to the driver and remember the
430 * driver is running and fully functional now.
431 *
432 * @param driver The driver which registered itself as running by the
433 * device manager.
434 */
435void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
436{
437 log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
438 driver->name);
439
440 /*
441 * Pass devices which have been already assigned to the driver to the
442 * driver.
443 */
444 pass_devices_to_driver(driver, tree);
445}
446
447/** Initialize device driver structure.
448 *
449 * @param drv The device driver structure.
450 */
451void init_driver(driver_t *drv)
452{
453 assert(drv != NULL);
454
455 memset(drv, 0, sizeof(driver_t));
456 list_initialize(&drv->match_ids.ids);
457 list_initialize(&drv->devices);
458 fibril_mutex_initialize(&drv->driver_mutex);
459 drv->sess = NULL;
460}
461
462/** Device driver structure clean-up.
463 *
464 * @param drv The device driver structure.
465 */
466void clean_driver(driver_t *drv)
467{
468 assert(drv != NULL);
469
470 free(drv->name);
471 free(drv->binary_path);
472
473 clean_match_ids(&drv->match_ids);
474
475 init_driver(drv);
476}
477
478/** Delete device driver structure.
479 *
480 * @param drv The device driver structure.
481 */
482void delete_driver(driver_t *drv)
483{
484 assert(drv != NULL);
485
486 clean_driver(drv);
487 free(drv);
488}
489
490/** Find suitable driver for a device and assign the driver to it.
491 *
492 * @param node The device node of the device in the device tree.
493 * @param drivers_list The list of available drivers.
494 * @return True if the suitable driver is found and
495 * successfully assigned to the device, false otherwise.
496 */
497bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
498 dev_tree_t *tree)
499{
500 assert(dev != NULL);
501 assert(drivers_list != NULL);
502 assert(tree != NULL);
503
504 /*
505 * Find the driver which is the most suitable for handling this device.
506 */
507 driver_t *drv = find_best_match_driver(drivers_list, dev);
508 if (drv == NULL) {
509 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
510 dev->pfun->pathname);
511 return false;
512 }
513
514 /* Attach the driver to the device. */
515 attach_driver(tree, dev, drv);
516
517 fibril_mutex_lock(&drv->driver_mutex);
518 if (drv->state == DRIVER_NOT_STARTED) {
519 /* Start the driver. */
520 start_driver(drv);
521 }
522 bool is_running = drv->state == DRIVER_RUNNING;
523 fibril_mutex_unlock(&drv->driver_mutex);
524
525 /* Notify the driver about the new device. */
526 if (is_running)
527 add_device(drv, dev, tree);
528
529 fibril_mutex_lock(&drv->driver_mutex);
530 fibril_mutex_unlock(&drv->driver_mutex);
531
532 fibril_rwlock_write_lock(&tree->rwlock);
533 if (dev->pfun != NULL) {
534 dev->pfun->state = FUN_ON_LINE;
535 }
536 fibril_rwlock_write_unlock(&tree->rwlock);
537 return true;
538}
539
540/** Pass a device to running driver.
541 *
542 * @param drv The driver's structure.
543 * @param node The device's node in the device tree.
544 */
545void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
546{
547 /*
548 * We do not expect to have driver's mutex locked as we do not
549 * access any structures that would affect driver_t.
550 */
551 log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
552 drv->name, dev->pfun->name);
553
554 /* Send the device to the driver. */
555 devman_handle_t parent_handle;
556 if (dev->pfun) {
557 parent_handle = dev->pfun->handle;
558 } else {
559 parent_handle = 0;
560 }
561
562 async_exch_t *exch = async_exchange_begin(drv->sess);
563
564 ipc_call_t answer;
565 aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
566 parent_handle, &answer);
567
568 /* Send the device name to the driver. */
569 sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
570 str_size(dev->pfun->name) + 1);
571
572 async_exchange_end(exch);
573
574 if (rc != EOK) {
575 /* TODO handle error */
576 }
577
578 /* Wait for answer from the driver. */
579 async_wait_for(req, &rc);
580
581 switch(rc) {
582 case EOK:
583 dev->state = DEVICE_USABLE;
584 break;
585 case ENOENT:
586 dev->state = DEVICE_NOT_PRESENT;
587 break;
588 default:
589 dev->state = DEVICE_INVALID;
590 break;
591 }
592
593 dev->passed_to_driver = true;
594
595 return;
596}
597
598int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
599{
600 async_exch_t *exch;
601 sysarg_t retval;
602 driver_t *drv;
603 devman_handle_t handle;
604
605 assert(dev != NULL);
606
607 log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
608
609 fibril_rwlock_read_lock(&tree->rwlock);
610 drv = dev->drv;
611 handle = dev->handle;
612 fibril_rwlock_read_unlock(&tree->rwlock);
613
614 exch = async_exchange_begin(drv->sess);
615 retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
616 async_exchange_end(exch);
617
618 return retval;
619}
620
621int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
622{
623 async_exch_t *exch;
624 sysarg_t retval;
625 driver_t *drv;
626 devman_handle_t handle;
627
628 assert(dev != NULL);
629
630 log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
631
632 fibril_rwlock_read_lock(&tree->rwlock);
633 drv = dev->drv;
634 handle = dev->handle;
635 fibril_rwlock_read_unlock(&tree->rwlock);
636
637 exch = async_exchange_begin(drv->sess);
638 retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
639 async_exchange_end(exch);
640
641 return retval;
642}
643
644int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
645{
646 async_exch_t *exch;
647 sysarg_t retval;
648 driver_t *drv;
649 devman_handle_t handle;
650
651 log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
652
653 fibril_rwlock_read_lock(&tree->rwlock);
654
655 if (fun->dev == NULL) {
656 /* XXX root function? */
657 fibril_rwlock_read_unlock(&tree->rwlock);
658 return EINVAL;
659 }
660
661 drv = fun->dev->drv;
662 handle = fun->handle;
663 fibril_rwlock_read_unlock(&tree->rwlock);
664
665 exch = async_exchange_begin(drv->sess);
666 retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
667 loc_exchange_end(exch);
668
669 return retval;
670}
671
672int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
673{
674 async_exch_t *exch;
675 sysarg_t retval;
676 driver_t *drv;
677 devman_handle_t handle;
678
679 log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
680
681 fibril_rwlock_read_lock(&tree->rwlock);
682 if (fun->dev == NULL) {
683 /* XXX root function? */
684 fibril_rwlock_read_unlock(&tree->rwlock);
685 return EINVAL;
686 }
687
688 drv = fun->dev->drv;
689 handle = fun->handle;
690 fibril_rwlock_read_unlock(&tree->rwlock);
691
692 exch = async_exchange_begin(drv->sess);
693 retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
694 loc_exchange_end(exch);
695
696 return retval;
697
698}
699
700/** Get list of registered drivers. */
701int driver_get_list(driver_list_t *driver_list, devman_handle_t *hdl_buf,
702 size_t buf_size, size_t *act_size)
703{
704 size_t act_cnt;
705 size_t buf_cnt;
706
707 fibril_mutex_lock(&driver_list->drivers_mutex);
708
709 buf_cnt = buf_size / sizeof(devman_handle_t);
710
711 act_cnt = list_count(&driver_list->drivers);
712 *act_size = act_cnt * sizeof(devman_handle_t);
713
714 if (buf_size % sizeof(devman_handle_t) != 0) {
715 fibril_mutex_unlock(&driver_list->drivers_mutex);
716 return EINVAL;
717 }
718
719 size_t pos = 0;
720 list_foreach(driver_list->drivers, drivers, driver_t, drv) {
721 if (pos < buf_cnt) {
722 hdl_buf[pos] = drv->handle;
723 }
724
725 pos++;
726 }
727
728 fibril_mutex_unlock(&driver_list->drivers_mutex);
729 return EOK;
730}
731
732/** Get list of device functions. */
733int driver_get_devices(driver_t *driver, devman_handle_t *hdl_buf,
734 size_t buf_size, size_t *act_size)
735{
736 size_t act_cnt;
737 size_t buf_cnt;
738
739 fibril_mutex_lock(&driver->driver_mutex);
740
741 buf_cnt = buf_size / sizeof(devman_handle_t);
742
743 act_cnt = list_count(&driver->devices);
744 *act_size = act_cnt * sizeof(devman_handle_t);
745
746 if (buf_size % sizeof(devman_handle_t) != 0) {
747 fibril_mutex_unlock(&driver->driver_mutex);
748 return EINVAL;
749 }
750
751 size_t pos = 0;
752 list_foreach(driver->devices, driver_devices, dev_node_t, dev) {
753 if (pos < buf_cnt) {
754 hdl_buf[pos] = dev->handle;
755 }
756
757 pos++;
758 }
759
760 fibril_mutex_unlock(&driver->driver_mutex);
761 return EOK;
762}
763
764/** @}
765 */
Note: See TracBrowser for help on using the repository browser.