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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ba1a2fd was ab108be4, checked in by Martin Decky <martin@…>, 15 years ago

fix several instances of possible NULL-pointer dereference and stale mutexes

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