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

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

improve naming conventions:
merge async_data_receive() and async_string_receive() into async_data_write_accept()
rename async_data_void() to async_data_write_void()
rename async_data_forward_fast() to async_data_write_forward_fast()
rename async_data_forward_n_m to async_data_write_forward_n_m

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