source: mainline/uspace/srv/devmap/devmap.c@ b72efe8

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

Separate list_t typedef from link_t (user-space part).

  • list_t represents lists
  • Use list_first(), list_last(), list_empty() where appropriate
  • Use list_foreach() where possible
  • assert_link_not_used()
  • usb_hid_report_path_free() shall not unlink the path, caller must do it
  • Property mode set to 100644
File size: 26.5 KB
Line 
1/*
2 * Copyright (c) 2007 Josef Cejka
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/**
30 * @defgroup devmap Device mapper.
31 * @brief HelenOS device mapper.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <ipc/services.h>
39#include <ns.h>
40#include <async.h>
41#include <stdio.h>
42#include <errno.h>
43#include <bool.h>
44#include <fibril_synch.h>
45#include <stdlib.h>
46#include <str.h>
47#include <ipc/devmap.h>
48#include <assert.h>
49
50#define NAME "devmap"
51#define NULL_DEVICES 256
52
53/** Representation of device driver.
54 *
55 * Each driver is responsible for a set of devices.
56 *
57 */
58typedef struct {
59 /** Link to drivers_list */
60 link_t drivers;
61
62 /** List of devices controlled by this driver */
63 list_t devices;
64
65 /** Session asociated with this driver */
66 async_sess_t *sess;
67
68 /** Device driver name */
69 char *name;
70
71 /** Fibril mutex for list of devices owned by this driver */
72 fibril_mutex_t devices_mutex;
73} devmap_driver_t;
74
75/** Info about registered namespaces
76 *
77 */
78typedef struct {
79 /** Link to namespaces_list */
80 link_t namespaces;
81
82 /** Unique namespace identifier */
83 devmap_handle_t handle;
84
85 /** Namespace name */
86 char *name;
87
88 /** Reference count */
89 size_t refcnt;
90} devmap_namespace_t;
91
92/** Info about registered device
93 *
94 */
95typedef struct {
96 /** Link to global list of devices (devices_list) */
97 link_t devices;
98 /** Link to driver list of devices (devmap_driver_t.devices) */
99 link_t driver_devices;
100 /** Unique device identifier */
101 devmap_handle_t handle;
102 /** Device namespace */
103 devmap_namespace_t *namespace;
104 /** Device name */
105 char *name;
106 /** Device driver handling this device */
107 devmap_driver_t *driver;
108 /** Use this interface when forwarding to driver. */
109 sysarg_t forward_interface;
110} devmap_device_t;
111
112LIST_INITIALIZE(devices_list);
113LIST_INITIALIZE(namespaces_list);
114LIST_INITIALIZE(drivers_list);
115
116/* Locking order:
117 * drivers_list_mutex
118 * devices_list_mutex
119 * (devmap_driver_t *)->devices_mutex
120 * create_handle_mutex
121 **/
122
123static FIBRIL_MUTEX_INITIALIZE(devices_list_mutex);
124static FIBRIL_CONDVAR_INITIALIZE(devices_list_cv);
125static FIBRIL_MUTEX_INITIALIZE(drivers_list_mutex);
126static FIBRIL_MUTEX_INITIALIZE(create_handle_mutex);
127static FIBRIL_MUTEX_INITIALIZE(null_devices_mutex);
128
129static devmap_handle_t last_handle = 0;
130static devmap_device_t *null_devices[NULL_DEVICES];
131
132/*
133 * Dummy list for null devices. This is necessary so that null devices can
134 * be used just as any other devices, e.g. in devmap_device_unregister_core().
135 */
136static LIST_INITIALIZE(dummy_null_driver_devices);
137
138static devmap_handle_t devmap_create_handle(void)
139{
140 /* TODO: allow reusing old handles after their unregistration
141 * and implement some version of LRU algorithm, avoid overflow
142 */
143
144 fibril_mutex_lock(&create_handle_mutex);
145 last_handle++;
146 fibril_mutex_unlock(&create_handle_mutex);
147
148 return last_handle;
149}
150
151/** Convert fully qualified device name to namespace and device name.
152 *
153 * A fully qualified device name can be either a plain device name
154 * (then the namespace is considered to be an empty string) or consist
155 * of two components separated by a slash. No more than one slash
156 * is allowed.
157 *
158 */
159static bool devmap_fqdn_split(const char *fqdn, char **ns_name, char **name)
160{
161 size_t cnt = 0;
162 size_t slash_offset = 0;
163 size_t slash_after = 0;
164
165 size_t offset = 0;
166 size_t offset_prev = 0;
167 wchar_t c;
168
169 while ((c = str_decode(fqdn, &offset, STR_NO_LIMIT)) != 0) {
170 if (c == '/') {
171 cnt++;
172 slash_offset = offset_prev;
173 slash_after = offset;
174 }
175 offset_prev = offset;
176 }
177
178 /* More than one slash */
179 if (cnt > 1)
180 return false;
181
182 /* No slash -> namespace is empty */
183 if (cnt == 0) {
184 *ns_name = str_dup("");
185 if (*ns_name == NULL)
186 return false;
187
188 *name = str_dup(fqdn);
189 if (*name == NULL) {
190 free(*ns_name);
191 return false;
192 }
193
194 if (str_cmp(*name, "") == 0) {
195 free(*name);
196 free(*ns_name);
197 return false;
198 }
199
200 return true;
201 }
202
203 /* Exactly one slash */
204 *ns_name = str_ndup(fqdn, slash_offset);
205 if (*ns_name == NULL)
206 return false;
207
208 *name = str_dup(fqdn + slash_after);
209 if (*name == NULL) {
210 free(*ns_name);
211 return false;
212 }
213
214 if (str_cmp(*name, "") == 0) {
215 free(*name);
216 free(*ns_name);
217 return false;
218 }
219
220 return true;
221}
222
223/** Find namespace with given name. */
224static devmap_namespace_t *devmap_namespace_find_name(const char *name)
225{
226 assert(fibril_mutex_is_locked(&devices_list_mutex));
227
228 list_foreach(namespaces_list, item) {
229 devmap_namespace_t *namespace =
230 list_get_instance(item, devmap_namespace_t, namespaces);
231 if (str_cmp(namespace->name, name) == 0)
232 return namespace;
233 }
234
235 return NULL;
236}
237
238/** Find namespace with given handle.
239 *
240 * @todo: use hash table
241 *
242 */
243static devmap_namespace_t *devmap_namespace_find_handle(devmap_handle_t handle)
244{
245 assert(fibril_mutex_is_locked(&devices_list_mutex));
246
247 list_foreach(namespaces_list, item) {
248 devmap_namespace_t *namespace =
249 list_get_instance(item, devmap_namespace_t, namespaces);
250 if (namespace->handle == handle)
251 return namespace;
252 }
253
254 return NULL;
255}
256
257/** Find device with given name. */
258static devmap_device_t *devmap_device_find_name(const char *ns_name,
259 const char *name)
260{
261 assert(fibril_mutex_is_locked(&devices_list_mutex));
262
263 list_foreach(devices_list, item) {
264 devmap_device_t *device =
265 list_get_instance(item, devmap_device_t, devices);
266 if ((str_cmp(device->namespace->name, ns_name) == 0)
267 && (str_cmp(device->name, name) == 0))
268 return device;
269 }
270
271 return NULL;
272}
273
274/** Find device with given handle.
275 *
276 * @todo: use hash table
277 *
278 */
279static devmap_device_t *devmap_device_find_handle(devmap_handle_t handle)
280{
281 assert(fibril_mutex_is_locked(&devices_list_mutex));
282
283 list_foreach(devices_list, item) {
284 devmap_device_t *device =
285 list_get_instance(item, devmap_device_t, devices);
286 if (device->handle == handle)
287 return device;
288 }
289
290 return NULL;
291}
292
293/** Create a namespace (if not already present). */
294static devmap_namespace_t *devmap_namespace_create(const char *ns_name)
295{
296 devmap_namespace_t *namespace;
297
298 assert(fibril_mutex_is_locked(&devices_list_mutex));
299
300 namespace = devmap_namespace_find_name(ns_name);
301 if (namespace != NULL)
302 return namespace;
303
304 namespace = (devmap_namespace_t *) malloc(sizeof(devmap_namespace_t));
305 if (namespace == NULL)
306 return NULL;
307
308 namespace->name = str_dup(ns_name);
309 if (namespace->name == NULL) {
310 free(namespace);
311 return NULL;
312 }
313
314 namespace->handle = devmap_create_handle();
315 namespace->refcnt = 0;
316
317 /*
318 * Insert new namespace into list of registered namespaces
319 */
320 list_append(&(namespace->namespaces), &namespaces_list);
321
322 return namespace;
323}
324
325/** Destroy a namespace (if it is no longer needed). */
326static void devmap_namespace_destroy(devmap_namespace_t *namespace)
327{
328 assert(fibril_mutex_is_locked(&devices_list_mutex));
329
330 if (namespace->refcnt == 0) {
331 list_remove(&(namespace->namespaces));
332
333 free(namespace->name);
334 free(namespace);
335 }
336}
337
338/** Increase namespace reference count by including device. */
339static void devmap_namespace_addref(devmap_namespace_t *namespace,
340 devmap_device_t *device)
341{
342 assert(fibril_mutex_is_locked(&devices_list_mutex));
343
344 device->namespace = namespace;
345 namespace->refcnt++;
346}
347
348/** Decrease namespace reference count. */
349static void devmap_namespace_delref(devmap_namespace_t *namespace)
350{
351 assert(fibril_mutex_is_locked(&devices_list_mutex));
352
353 namespace->refcnt--;
354 devmap_namespace_destroy(namespace);
355}
356
357/** Unregister device and free it. */
358static void devmap_device_unregister_core(devmap_device_t *device)
359{
360 assert(fibril_mutex_is_locked(&devices_list_mutex));
361
362 devmap_namespace_delref(device->namespace);
363 list_remove(&(device->devices));
364 list_remove(&(device->driver_devices));
365
366 free(device->name);
367 free(device);
368}
369
370/**
371 * Read info about new driver and add it into linked list of registered
372 * drivers.
373 */
374static devmap_driver_t *devmap_driver_register(void)
375{
376 ipc_call_t icall;
377 ipc_callid_t iid = async_get_call(&icall);
378
379 if (IPC_GET_IMETHOD(icall) != DEVMAP_DRIVER_REGISTER) {
380 async_answer_0(iid, EREFUSED);
381 return NULL;
382 }
383
384 devmap_driver_t *driver =
385 (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
386 if (driver == NULL) {
387 async_answer_0(iid, ENOMEM);
388 return NULL;
389 }
390
391 /*
392 * Get driver name
393 */
394 int rc = async_data_write_accept((void **) &driver->name, true, 0,
395 DEVMAP_NAME_MAXLEN, 0, NULL);
396 if (rc != EOK) {
397 free(driver);
398 async_answer_0(iid, rc);
399 return NULL;
400 }
401
402 /*
403 * Create connection to the driver
404 */
405 driver->sess = async_callback_receive(EXCHANGE_SERIALIZE);
406 if (!driver->sess) {
407 free(driver->name);
408 free(driver);
409 async_answer_0(iid, ENOTSUP);
410 return NULL;
411 }
412
413 /*
414 * Initialize mutex for list of devices
415 * owned by this driver
416 */
417 fibril_mutex_initialize(&driver->devices_mutex);
418
419 /*
420 * Initialize list of asociated devices
421 */
422 list_initialize(&driver->devices);
423
424 link_initialize(&driver->drivers);
425
426 fibril_mutex_lock(&drivers_list_mutex);
427
428 /* TODO:
429 * Check that no driver with name equal to
430 * driver->name is registered
431 */
432
433 /*
434 * Insert new driver into list of registered drivers
435 */
436 list_append(&(driver->drivers), &drivers_list);
437 fibril_mutex_unlock(&drivers_list_mutex);
438
439 async_answer_0(iid, EOK);
440
441 return driver;
442}
443
444/**
445 * Unregister device driver, unregister all its devices and free driver
446 * structure.
447 *
448 */
449static int devmap_driver_unregister(devmap_driver_t *driver)
450{
451 if (driver == NULL)
452 return EEXISTS;
453
454 fibril_mutex_lock(&drivers_list_mutex);
455
456 if (driver->sess)
457 async_hangup(driver->sess);
458
459 /* Remove it from list of drivers */
460 list_remove(&(driver->drivers));
461
462 /* Unregister all its devices */
463 fibril_mutex_lock(&devices_list_mutex);
464 fibril_mutex_lock(&driver->devices_mutex);
465
466 while (!list_empty(&driver->devices)) {
467 devmap_device_t *device = list_get_instance(
468 list_first(&driver->devices), devmap_device_t,
469 driver_devices);
470 devmap_device_unregister_core(device);
471 }
472
473 fibril_mutex_unlock(&driver->devices_mutex);
474 fibril_mutex_unlock(&devices_list_mutex);
475 fibril_mutex_unlock(&drivers_list_mutex);
476
477 /* Free name and driver */
478 if (driver->name != NULL)
479 free(driver->name);
480
481 free(driver);
482
483 return EOK;
484}
485
486/** Register instance of device
487 *
488 */
489static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
490 devmap_driver_t *driver)
491{
492 if (driver == NULL) {
493 async_answer_0(iid, EREFUSED);
494 return;
495 }
496
497 /* Create new device entry */
498 devmap_device_t *device =
499 (devmap_device_t *) malloc(sizeof(devmap_device_t));
500 if (device == NULL) {
501 async_answer_0(iid, ENOMEM);
502 return;
503 }
504
505 /* Set the interface, if any. */
506 device->forward_interface = IPC_GET_ARG1(*icall);
507
508 /* Get fqdn */
509 char *fqdn;
510 int rc = async_data_write_accept((void **) &fqdn, true, 0,
511 DEVMAP_NAME_MAXLEN, 0, NULL);
512 if (rc != EOK) {
513 free(device);
514 async_answer_0(iid, rc);
515 return;
516 }
517
518 char *ns_name;
519 if (!devmap_fqdn_split(fqdn, &ns_name, &device->name)) {
520 free(fqdn);
521 free(device);
522 async_answer_0(iid, EINVAL);
523 return;
524 }
525
526 free(fqdn);
527
528 fibril_mutex_lock(&devices_list_mutex);
529
530 devmap_namespace_t *namespace = devmap_namespace_create(ns_name);
531 free(ns_name);
532 if (namespace == NULL) {
533 fibril_mutex_unlock(&devices_list_mutex);
534 free(device->name);
535 free(device);
536 async_answer_0(iid, ENOMEM);
537 return;
538 }
539
540 link_initialize(&device->devices);
541 link_initialize(&device->driver_devices);
542
543 /* Check that device is not already registered */
544 if (devmap_device_find_name(namespace->name, device->name) != NULL) {
545 printf("%s: Device '%s/%s' already registered\n", NAME,
546 namespace->name, device->name);
547 devmap_namespace_destroy(namespace);
548 fibril_mutex_unlock(&devices_list_mutex);
549 free(device->name);
550 free(device);
551 async_answer_0(iid, EEXISTS);
552 return;
553 }
554
555 /* Get unique device handle */
556 device->handle = devmap_create_handle();
557
558 devmap_namespace_addref(namespace, device);
559 device->driver = driver;
560
561 /* Insert device into list of all devices */
562 list_append(&device->devices, &devices_list);
563
564 /* Insert device into list of devices that belog to one driver */
565 fibril_mutex_lock(&device->driver->devices_mutex);
566
567 list_append(&device->driver_devices, &device->driver->devices);
568
569 fibril_mutex_unlock(&device->driver->devices_mutex);
570 fibril_condvar_broadcast(&devices_list_cv);
571 fibril_mutex_unlock(&devices_list_mutex);
572
573 async_answer_1(iid, EOK, device->handle);
574}
575
576/**
577 *
578 */
579static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
580 devmap_driver_t *driver)
581{
582 /* TODO */
583 return EOK;
584}
585
586/** Connect client to the device.
587 *
588 * Find device driver owning requested device and forward
589 * the message to it.
590 *
591 */
592static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
593{
594 fibril_mutex_lock(&devices_list_mutex);
595
596 /*
597 * Get handle from request
598 */
599 devmap_handle_t handle = IPC_GET_ARG2(*call);
600 devmap_device_t *dev = devmap_device_find_handle(handle);
601
602 if ((dev == NULL) || (dev->driver == NULL) || (!dev->driver->sess)) {
603 fibril_mutex_unlock(&devices_list_mutex);
604 async_answer_0(callid, ENOENT);
605 return;
606 }
607
608 async_exch_t *exch = async_exchange_begin(dev->driver->sess);
609
610 if (dev->forward_interface == 0)
611 async_forward_fast(callid, exch, dev->handle, 0, 0, IPC_FF_NONE);
612 else
613 async_forward_fast(callid, exch, dev->forward_interface,
614 dev->handle, 0, IPC_FF_NONE);
615
616 async_exchange_end(exch);
617
618 fibril_mutex_unlock(&devices_list_mutex);
619}
620
621/** Find handle for device instance identified by name.
622 *
623 * In answer will be send EOK and device handle in arg1 or a error
624 * code from errno.h.
625 *
626 */
627static void devmap_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
628{
629 char *fqdn;
630
631 /* Get fqdn */
632 int rc = async_data_write_accept((void **) &fqdn, true, 0,
633 DEVMAP_NAME_MAXLEN, 0, NULL);
634 if (rc != EOK) {
635 async_answer_0(iid, rc);
636 return;
637 }
638
639 char *ns_name;
640 char *name;
641 if (!devmap_fqdn_split(fqdn, &ns_name, &name)) {
642 free(fqdn);
643 async_answer_0(iid, EINVAL);
644 return;
645 }
646
647 free(fqdn);
648
649 fibril_mutex_lock(&devices_list_mutex);
650 const devmap_device_t *dev;
651
652recheck:
653
654 /*
655 * Find device name in the list of known devices.
656 */
657 dev = devmap_device_find_name(ns_name, name);
658
659 /*
660 * Device was not found.
661 */
662 if (dev == NULL) {
663 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
664 /* Blocking lookup */
665 fibril_condvar_wait(&devices_list_cv,
666 &devices_list_mutex);
667 goto recheck;
668 }
669
670 async_answer_0(iid, ENOENT);
671 free(ns_name);
672 free(name);
673 fibril_mutex_unlock(&devices_list_mutex);
674 return;
675 }
676
677 async_answer_1(iid, EOK, dev->handle);
678
679 fibril_mutex_unlock(&devices_list_mutex);
680 free(ns_name);
681 free(name);
682}
683
684/** Find handle for namespace identified by name.
685 *
686 * In answer will be send EOK and device handle in arg1 or a error
687 * code from errno.h.
688 *
689 */
690static void devmap_namespace_get_handle(ipc_callid_t iid, ipc_call_t *icall)
691{
692 char *name;
693
694 /* Get device name */
695 int rc = async_data_write_accept((void **) &name, true, 0,
696 DEVMAP_NAME_MAXLEN, 0, NULL);
697 if (rc != EOK) {
698 async_answer_0(iid, rc);
699 return;
700 }
701
702 fibril_mutex_lock(&devices_list_mutex);
703 const devmap_namespace_t *namespace;
704
705recheck:
706
707 /*
708 * Find namespace name in the list of known namespaces.
709 */
710 namespace = devmap_namespace_find_name(name);
711
712 /*
713 * Namespace was not found.
714 */
715 if (namespace == NULL) {
716 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
717 /* Blocking lookup */
718 fibril_condvar_wait(&devices_list_cv,
719 &devices_list_mutex);
720 goto recheck;
721 }
722
723 async_answer_0(iid, ENOENT);
724 free(name);
725 fibril_mutex_unlock(&devices_list_mutex);
726 return;
727 }
728
729 async_answer_1(iid, EOK, namespace->handle);
730
731 fibril_mutex_unlock(&devices_list_mutex);
732 free(name);
733}
734
735static void devmap_handle_probe(ipc_callid_t iid, ipc_call_t *icall)
736{
737 fibril_mutex_lock(&devices_list_mutex);
738
739 devmap_namespace_t *namespace =
740 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
741 if (namespace == NULL) {
742 devmap_device_t *dev =
743 devmap_device_find_handle(IPC_GET_ARG1(*icall));
744 if (dev == NULL)
745 async_answer_1(iid, EOK, DEV_HANDLE_NONE);
746 else
747 async_answer_1(iid, EOK, DEV_HANDLE_DEVICE);
748 } else
749 async_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE);
750
751 fibril_mutex_unlock(&devices_list_mutex);
752}
753
754static void devmap_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
755{
756 fibril_mutex_lock(&devices_list_mutex);
757 async_answer_1(iid, EOK, list_count(&namespaces_list));
758 fibril_mutex_unlock(&devices_list_mutex);
759}
760
761static void devmap_get_device_count(ipc_callid_t iid, ipc_call_t *icall)
762{
763 fibril_mutex_lock(&devices_list_mutex);
764
765 devmap_namespace_t *namespace =
766 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
767 if (namespace == NULL)
768 async_answer_0(iid, EEXISTS);
769 else
770 async_answer_1(iid, EOK, namespace->refcnt);
771
772 fibril_mutex_unlock(&devices_list_mutex);
773}
774
775static void devmap_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
776{
777 ipc_callid_t callid;
778 size_t size;
779 if (!async_data_read_receive(&callid, &size)) {
780 async_answer_0(callid, EREFUSED);
781 async_answer_0(iid, EREFUSED);
782 return;
783 }
784
785 if ((size % sizeof(dev_desc_t)) != 0) {
786 async_answer_0(callid, EINVAL);
787 async_answer_0(iid, EINVAL);
788 return;
789 }
790
791 fibril_mutex_lock(&devices_list_mutex);
792
793 size_t count = size / sizeof(dev_desc_t);
794 if (count != list_count(&namespaces_list)) {
795 fibril_mutex_unlock(&devices_list_mutex);
796 async_answer_0(callid, EOVERFLOW);
797 async_answer_0(iid, EOVERFLOW);
798 return;
799 }
800
801 dev_desc_t *desc = (dev_desc_t *) malloc(size);
802 if (desc == NULL) {
803 fibril_mutex_unlock(&devices_list_mutex);
804 async_answer_0(callid, ENOMEM);
805 async_answer_0(iid, ENOMEM);
806 return;
807 }
808
809 size_t pos = 0;
810 list_foreach(namespaces_list, item) {
811 devmap_namespace_t *namespace =
812 list_get_instance(item, devmap_namespace_t, namespaces);
813
814 desc[pos].handle = namespace->handle;
815 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, namespace->name);
816 pos++;
817 }
818
819 sysarg_t retval = async_data_read_finalize(callid, desc, size);
820
821 free(desc);
822 fibril_mutex_unlock(&devices_list_mutex);
823
824 async_answer_0(iid, retval);
825}
826
827static void devmap_get_devices(ipc_callid_t iid, ipc_call_t *icall)
828{
829 /* FIXME: Use faster algorithm which can make better use
830 of namespaces */
831
832 ipc_callid_t callid;
833 size_t size;
834 if (!async_data_read_receive(&callid, &size)) {
835 async_answer_0(callid, EREFUSED);
836 async_answer_0(iid, EREFUSED);
837 return;
838 }
839
840 if ((size % sizeof(dev_desc_t)) != 0) {
841 async_answer_0(callid, EINVAL);
842 async_answer_0(iid, EINVAL);
843 return;
844 }
845
846 fibril_mutex_lock(&devices_list_mutex);
847
848 devmap_namespace_t *namespace =
849 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
850 if (namespace == NULL) {
851 fibril_mutex_unlock(&devices_list_mutex);
852 async_answer_0(callid, ENOENT);
853 async_answer_0(iid, ENOENT);
854 return;
855 }
856
857 size_t count = size / sizeof(dev_desc_t);
858 if (count != namespace->refcnt) {
859 fibril_mutex_unlock(&devices_list_mutex);
860 async_answer_0(callid, EOVERFLOW);
861 async_answer_0(iid, EOVERFLOW);
862 return;
863 }
864
865 dev_desc_t *desc = (dev_desc_t *) malloc(size);
866 if (desc == NULL) {
867 fibril_mutex_unlock(&devices_list_mutex);
868 async_answer_0(callid, ENOMEM);
869 async_answer_0(iid, EREFUSED);
870 return;
871 }
872
873 size_t pos = 0;
874 list_foreach(devices_list, item) {
875 devmap_device_t *device =
876 list_get_instance(item, devmap_device_t, devices);
877
878 if (device->namespace == namespace) {
879 desc[pos].handle = device->handle;
880 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, device->name);
881 pos++;
882 }
883 }
884
885 sysarg_t retval = async_data_read_finalize(callid, desc, size);
886
887 free(desc);
888 fibril_mutex_unlock(&devices_list_mutex);
889
890 async_answer_0(iid, retval);
891}
892
893static void devmap_null_create(ipc_callid_t iid, ipc_call_t *icall)
894{
895 fibril_mutex_lock(&null_devices_mutex);
896
897 unsigned int i;
898 bool fnd = false;
899
900 for (i = 0; i < NULL_DEVICES; i++) {
901 if (null_devices[i] == NULL) {
902 fnd = true;
903 break;
904 }
905 }
906
907 if (!fnd) {
908 fibril_mutex_unlock(&null_devices_mutex);
909 async_answer_0(iid, ENOMEM);
910 return;
911 }
912
913 char null[DEVMAP_NAME_MAXLEN];
914 snprintf(null, DEVMAP_NAME_MAXLEN, "%u", i);
915
916 char *dev_name = str_dup(null);
917 if (dev_name == NULL) {
918 fibril_mutex_unlock(&null_devices_mutex);
919 async_answer_0(iid, ENOMEM);
920 return;
921 }
922
923 devmap_device_t *device =
924 (devmap_device_t *) malloc(sizeof(devmap_device_t));
925 if (device == NULL) {
926 fibril_mutex_unlock(&null_devices_mutex);
927 async_answer_0(iid, ENOMEM);
928 return;
929 }
930
931 fibril_mutex_lock(&devices_list_mutex);
932
933 devmap_namespace_t *namespace = devmap_namespace_create("null");
934 if (namespace == NULL) {
935 fibril_mutex_lock(&devices_list_mutex);
936 fibril_mutex_unlock(&null_devices_mutex);
937 async_answer_0(iid, ENOMEM);
938 return;
939 }
940
941 link_initialize(&device->devices);
942 link_initialize(&device->driver_devices);
943
944 /* Get unique device handle */
945 device->handle = devmap_create_handle();
946 device->driver = NULL;
947
948 devmap_namespace_addref(namespace, device);
949 device->name = dev_name;
950
951 /*
952 * Insert device into list of all devices and into null devices array.
953 * Insert device into a dummy list of null driver's devices so that it
954 * can be safely removed later.
955 */
956 list_append(&device->devices, &devices_list);
957 list_append(&device->driver_devices, &dummy_null_driver_devices);
958 null_devices[i] = device;
959
960 fibril_mutex_unlock(&devices_list_mutex);
961 fibril_mutex_unlock(&null_devices_mutex);
962
963 async_answer_1(iid, EOK, (sysarg_t) i);
964}
965
966static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
967{
968 sysarg_t i = IPC_GET_ARG1(*icall);
969 if (i >= NULL_DEVICES) {
970 async_answer_0(iid, ELIMIT);
971 return;
972 }
973
974 fibril_mutex_lock(&null_devices_mutex);
975
976 if (null_devices[i] == NULL) {
977 fibril_mutex_unlock(&null_devices_mutex);
978 async_answer_0(iid, ENOENT);
979 return;
980 }
981
982 fibril_mutex_lock(&devices_list_mutex);
983 devmap_device_unregister_core(null_devices[i]);
984 fibril_mutex_unlock(&devices_list_mutex);
985
986 null_devices[i] = NULL;
987
988 fibril_mutex_unlock(&null_devices_mutex);
989 async_answer_0(iid, EOK);
990}
991
992/** Initialize device mapper.
993 *
994 *
995 */
996static bool devmap_init(void)
997{
998 fibril_mutex_lock(&null_devices_mutex);
999
1000 unsigned int i;
1001 for (i = 0; i < NULL_DEVICES; i++)
1002 null_devices[i] = NULL;
1003
1004 fibril_mutex_unlock(&null_devices_mutex);
1005
1006 return true;
1007}
1008
1009/** Handle connection with device driver.
1010 *
1011 */
1012static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
1013{
1014 /* Accept connection */
1015 async_answer_0(iid, EOK);
1016
1017 devmap_driver_t *driver = devmap_driver_register();
1018 if (driver == NULL)
1019 return;
1020
1021 while (true) {
1022 ipc_call_t call;
1023 ipc_callid_t callid = async_get_call(&call);
1024
1025 if (!IPC_GET_IMETHOD(call))
1026 break;
1027
1028 switch (IPC_GET_IMETHOD(call)) {
1029 case DEVMAP_DRIVER_UNREGISTER:
1030 if (NULL == driver)
1031 async_answer_0(callid, ENOENT);
1032 else
1033 async_answer_0(callid, EOK);
1034 break;
1035 case DEVMAP_DEVICE_REGISTER:
1036 /* Register one instance of device */
1037 devmap_device_register(callid, &call, driver);
1038 break;
1039 case DEVMAP_DEVICE_UNREGISTER:
1040 /* Remove instance of device identified by handler */
1041 devmap_device_unregister(callid, &call, driver);
1042 break;
1043 case DEVMAP_DEVICE_GET_HANDLE:
1044 devmap_device_get_handle(callid, &call);
1045 break;
1046 case DEVMAP_NAMESPACE_GET_HANDLE:
1047 devmap_namespace_get_handle(callid, &call);
1048 break;
1049 default:
1050 async_answer_0(callid, ENOENT);
1051 }
1052 }
1053
1054 if (driver != NULL) {
1055 /*
1056 * Unregister the device driver and all its devices.
1057 */
1058 devmap_driver_unregister(driver);
1059 driver = NULL;
1060 }
1061}
1062
1063/** Handle connection with device client.
1064 *
1065 */
1066static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
1067{
1068 /* Accept connection */
1069 async_answer_0(iid, EOK);
1070
1071 while (true) {
1072 ipc_call_t call;
1073 ipc_callid_t callid = async_get_call(&call);
1074
1075 if (!IPC_GET_IMETHOD(call))
1076 break;
1077
1078 switch (IPC_GET_IMETHOD(call)) {
1079 case DEVMAP_DEVICE_GET_HANDLE:
1080 devmap_device_get_handle(callid, &call);
1081 break;
1082 case DEVMAP_NAMESPACE_GET_HANDLE:
1083 devmap_namespace_get_handle(callid, &call);
1084 break;
1085 case DEVMAP_HANDLE_PROBE:
1086 devmap_handle_probe(callid, &call);
1087 break;
1088 case DEVMAP_NULL_CREATE:
1089 devmap_null_create(callid, &call);
1090 break;
1091 case DEVMAP_NULL_DESTROY:
1092 devmap_null_destroy(callid, &call);
1093 break;
1094 case DEVMAP_GET_NAMESPACE_COUNT:
1095 devmap_get_namespace_count(callid, &call);
1096 break;
1097 case DEVMAP_GET_DEVICE_COUNT:
1098 devmap_get_device_count(callid, &call);
1099 break;
1100 case DEVMAP_GET_NAMESPACES:
1101 devmap_get_namespaces(callid, &call);
1102 break;
1103 case DEVMAP_GET_DEVICES:
1104 devmap_get_devices(callid, &call);
1105 break;
1106 default:
1107 async_answer_0(callid, ENOENT);
1108 }
1109 }
1110}
1111
1112/** Function for handling connections to devmap
1113 *
1114 */
1115static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1116{
1117 /* Select interface */
1118 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
1119 case DEVMAP_DRIVER:
1120 devmap_connection_driver(iid, icall);
1121 break;
1122 case DEVMAP_CLIENT:
1123 devmap_connection_client(iid, icall);
1124 break;
1125 case DEVMAP_CONNECT_TO_DEVICE:
1126 /* Connect client to selected device */
1127 devmap_forward(iid, icall);
1128 break;
1129 default:
1130 /* No such interface */
1131 async_answer_0(iid, ENOENT);
1132 }
1133}
1134
1135/**
1136 *
1137 */
1138int main(int argc, char *argv[])
1139{
1140 printf("%s: HelenOS Device Mapper\n", NAME);
1141
1142 if (!devmap_init()) {
1143 printf("%s: Error while initializing service\n", NAME);
1144 return -1;
1145 }
1146
1147 /* Set a handler of incomming connections */
1148 async_set_client_connection(devmap_connection);
1149
1150 /* Register device mapper at naming service */
1151 if (service_register(SERVICE_DEVMAP) != EOK)
1152 return -1;
1153
1154 printf("%s: Accepting connections\n", NAME);
1155 async_manager();
1156
1157 /* Never reached */
1158 return 0;
1159}
1160
1161/**
1162 * @}
1163 */
Note: See TracBrowser for help on using the repository browser.