source: mainline/uspace/srv/devmap/devmap.c@ 63e0bdd

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

Null devices need to be in some driver_devices list too,
otherwise devmap will crash when devmap_null_destroy() is called.

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