source: mainline/uspace/srv/devmap/devmap.c@ 1d1bb0f

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

Add extra argument to async connection handlers that can be used for passing
information from async_connect_to_me() to the handler.

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