source: mainline/uspace/srv/devmap/devmap.c@ 78ffb70

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 78ffb70 was 40dc422, checked in by Jakub Jermar <jakub@…>, 15 years ago

Merge from lp:~vojtech-horky/helenos/ddf-fixes.

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