source: mainline/uspace/srv/devmap/devmap.c@ 01b87dc5

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

Deploy the fibril_{mutex,rwlock}_[*_]is_locked() functions in a few locations.

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