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

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

more consistent naming scheme:

async_data_blob_receive → async_data_receive
async_data_string_receive → async_string_receive
CLIPBOARD_TAG_BLOB → CLIPBOARD_TAG_DATA

async_data_receive can now check the granularity of the received data
async_string_receive can now return the raw size of the received data

replace several common patterns of async_data_write_receive/_finalize
with a single async_data_receive/_string_receive (this greatly improves
code readability)

  • Property mode set to 100644
File size: 26.0 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_string_receive(&driver->name, DEVMAP_NAME_MAXLEN, NULL);
399 if (rc != EOK) {
400 free(driver);
401 ipc_answer_0(iid, rc);
402 return NULL;
403 }
404
405 /* Initialize mutex for list of devices owned by this driver */
406 fibril_mutex_initialize(&driver->devices_mutex);
407
408 /*
409 * Initialize list of asociated devices
410 */
411 list_initialize(&driver->devices);
412
413 /*
414 * Create connection to the driver
415 */
416 ipc_call_t call;
417 ipc_callid_t callid = async_get_call(&call);
418
419 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
420 ipc_answer_0(callid, ENOTSUP);
421
422 free(driver->name);
423 free(driver);
424 ipc_answer_0(iid, ENOTSUP);
425 return NULL;
426 }
427
428 driver->phone = IPC_GET_ARG5(call);
429
430 ipc_answer_0(callid, EOK);
431
432 list_initialize(&(driver->drivers));
433
434 fibril_mutex_lock(&drivers_list_mutex);
435
436 /* TODO:
437 * check that no driver with name equal to driver->name is registered
438 */
439
440 /*
441 * Insert new driver into list of registered drivers
442 */
443 list_append(&(driver->drivers), &drivers_list);
444 fibril_mutex_unlock(&drivers_list_mutex);
445
446 ipc_answer_0(iid, EOK);
447
448 return driver;
449}
450
451/**
452 * Unregister device driver, unregister all its devices and free driver
453 * structure.
454 *
455 */
456static int devmap_driver_unregister(devmap_driver_t *driver)
457{
458 if (driver == NULL)
459 return EEXISTS;
460
461 fibril_mutex_lock(&drivers_list_mutex);
462
463 if (driver->phone != 0)
464 ipc_hangup(driver->phone);
465
466 /* Remove it from list of drivers */
467 list_remove(&(driver->drivers));
468
469 /* Unregister all its devices */
470 fibril_mutex_lock(&devices_list_mutex);
471 fibril_mutex_lock(&driver->devices_mutex);
472
473 while (!list_empty(&(driver->devices))) {
474 devmap_device_t *device = list_get_instance(driver->devices.next,
475 devmap_device_t, driver_devices);
476 devmap_device_unregister_core(device);
477 }
478
479 fibril_mutex_unlock(&driver->devices_mutex);
480 fibril_mutex_unlock(&devices_list_mutex);
481 fibril_mutex_unlock(&drivers_list_mutex);
482
483 /* free name and driver */
484 if (driver->name != NULL)
485 free(driver->name);
486
487 free(driver);
488
489 return EOK;
490}
491
492/** Register instance of device
493 *
494 */
495static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
496 devmap_driver_t *driver)
497{
498 if (driver == NULL) {
499 ipc_answer_0(iid, EREFUSED);
500 return;
501 }
502
503 /* Create new device entry */
504 devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
505 if (device == NULL) {
506 ipc_answer_0(iid, ENOMEM);
507 return;
508 }
509
510 /* Get fqdn */
511 char *fqdn;
512 int rc = async_string_receive(&fqdn, DEVMAP_NAME_MAXLEN, NULL);
513 if (rc != EOK) {
514 free(device);
515 ipc_answer_0(iid, rc);
516 return;
517 }
518
519 char *ns_name;
520 if (!devmap_fqdn_split(fqdn, &ns_name, &device->name)) {
521 free(fqdn);
522 free(device);
523 ipc_answer_0(iid, EINVAL);
524 return;
525 }
526
527 free(fqdn);
528
529 fibril_mutex_lock(&devices_list_mutex);
530
531 devmap_namespace_t *namespace = devmap_namespace_create(ns_name);
532 free(ns_name);
533 if (!namespace) {
534 fibril_mutex_unlock(&devices_list_mutex);
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(NAME ": Device '%s/%s' already registered\n", device->namespace, device->name);
546 devmap_namespace_destroy(namespace);
547 fibril_mutex_unlock(&devices_list_mutex);
548 free(device->namespace);
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 dev_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 ipc_answer_0(callid, ENOENT);
604 return;
605 }
606
607 ipc_forward_fast(callid, dev->driver->phone, dev->handle,
608 IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
609
610 fibril_mutex_unlock(&devices_list_mutex);
611}
612
613/** Find handle for device instance identified by name.
614 *
615 * In answer will be send EOK and device handle in arg1 or a error
616 * code from errno.h.
617 *
618 */
619static void devmap_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
620{
621 char *fqdn;
622
623 /* Get fqdn */
624 int rc = async_string_receive(&fqdn, DEVMAP_NAME_MAXLEN, NULL);
625 if (rc != EOK) {
626 ipc_answer_0(iid, rc);
627 return;
628 }
629
630 char *ns_name;
631 char *name;
632 if (!devmap_fqdn_split(fqdn, &ns_name, &name)) {
633 free(fqdn);
634 ipc_answer_0(iid, EINVAL);
635 return;
636 }
637
638 free(fqdn);
639
640 fibril_mutex_lock(&devices_list_mutex);
641 const devmap_device_t *dev;
642
643recheck:
644
645 /*
646 * Find device name in the list of known devices.
647 */
648 dev = devmap_device_find_name(ns_name, name);
649
650 /*
651 * Device was not found.
652 */
653 if (dev == NULL) {
654 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
655 /* Blocking lookup */
656 fibril_condvar_wait(&devices_list_cv,
657 &devices_list_mutex);
658 goto recheck;
659 }
660
661 ipc_answer_0(iid, ENOENT);
662 free(ns_name);
663 free(name);
664 fibril_mutex_unlock(&devices_list_mutex);
665 return;
666 }
667 fibril_mutex_unlock(&devices_list_mutex);
668
669 ipc_answer_1(iid, EOK, dev->handle);
670 free(ns_name);
671 free(name);
672}
673
674/** Find handle for namespace identified by name.
675 *
676 * In answer will be send EOK and device handle in arg1 or a error
677 * code from errno.h.
678 *
679 */
680static void devmap_namespace_get_handle(ipc_callid_t iid, ipc_call_t *icall)
681{
682 char *name;
683
684 /* Get device name */
685 int rc = async_string_receive(&name, DEVMAP_NAME_MAXLEN, NULL);
686 if (rc != EOK) {
687 ipc_answer_0(iid, rc);
688 return;
689 }
690
691 fibril_mutex_lock(&devices_list_mutex);
692 const devmap_namespace_t *namespace;
693
694recheck:
695
696 /*
697 * Find namespace name in the list of known namespaces.
698 */
699 namespace = devmap_namespace_find_name(name);
700
701 /*
702 * Namespace was not found.
703 */
704 if (namespace == NULL) {
705 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
706 /* Blocking lookup */
707 fibril_condvar_wait(&devices_list_cv,
708 &devices_list_mutex);
709 goto recheck;
710 }
711
712 ipc_answer_0(iid, ENOENT);
713 free(name);
714 fibril_mutex_unlock(&devices_list_mutex);
715 return;
716 }
717 fibril_mutex_unlock(&devices_list_mutex);
718
719 ipc_answer_1(iid, EOK, namespace->handle);
720 free(name);
721}
722
723static void devmap_handle_probe(ipc_callid_t iid, ipc_call_t *icall)
724{
725 fibril_mutex_lock(&devices_list_mutex);
726
727 devmap_namespace_t *namespace = devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
728 if (namespace == NULL) {
729 devmap_device_t *dev = devmap_device_find_handle(IPC_GET_ARG1(*icall));
730 if (dev == NULL)
731 ipc_answer_1(iid, EOK, DEV_HANDLE_NONE);
732 else
733 ipc_answer_1(iid, EOK, DEV_HANDLE_DEVICE);
734 } else
735 ipc_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE);
736
737 fibril_mutex_unlock(&devices_list_mutex);
738}
739
740static void devmap_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
741{
742 fibril_mutex_lock(&devices_list_mutex);
743 ipc_answer_1(iid, EOK, list_count(&namespaces_list));
744 fibril_mutex_unlock(&devices_list_mutex);
745}
746
747static void devmap_get_device_count(ipc_callid_t iid, ipc_call_t *icall)
748{
749 fibril_mutex_lock(&devices_list_mutex);
750
751 devmap_namespace_t *namespace = devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
752 if (namespace == NULL)
753 ipc_answer_0(iid, EEXISTS);
754 else
755 ipc_answer_1(iid, EOK, namespace->refcnt);
756
757 fibril_mutex_unlock(&devices_list_mutex);
758}
759
760static void devmap_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
761{
762 ipc_callid_t callid;
763 size_t size;
764 if (!async_data_read_receive(&callid, &size)) {
765 ipc_answer_0(callid, EREFUSED);
766 ipc_answer_0(iid, EREFUSED);
767 return;
768 }
769
770 if ((size % sizeof(dev_desc_t)) != 0) {
771 ipc_answer_0(callid, EINVAL);
772 ipc_answer_0(iid, EINVAL);
773 return;
774 }
775
776 fibril_mutex_lock(&devices_list_mutex);
777
778 size_t count = size / sizeof(dev_desc_t);
779 if (count != list_count(&namespaces_list)) {
780 ipc_answer_0(callid, EOVERFLOW);
781 ipc_answer_0(iid, EOVERFLOW);
782 return;
783 }
784
785 dev_desc_t *desc = (dev_desc_t *) malloc(size);
786 if (desc == NULL) {
787 ipc_answer_0(callid, ENOMEM);
788 ipc_answer_0(iid, ENOMEM);
789 return;
790 }
791
792 link_t *item = namespaces_list.next;
793 size_t pos = 0;
794 while (item != &namespaces_list) {
795 devmap_namespace_t *namespace = list_get_instance(item, devmap_namespace_t, namespaces);
796
797 desc[pos].handle = namespace->handle;
798 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, namespace->name);
799 pos++;
800
801 item = item->next;
802 }
803
804 ipcarg_t retval = async_data_read_finalize(callid, desc, size);
805
806 free(desc);
807 fibril_mutex_unlock(&devices_list_mutex);
808
809 ipc_answer_0(iid, retval);
810}
811
812static void devmap_get_devices(ipc_callid_t iid, ipc_call_t *icall)
813{
814 /* FIXME: Use faster algorithm which can make better use
815 of namespaces */
816
817 ipc_callid_t callid;
818 size_t size;
819 if (!async_data_read_receive(&callid, &size)) {
820 ipc_answer_0(callid, EREFUSED);
821 ipc_answer_0(iid, EREFUSED);
822 return;
823 }
824
825 if ((size % sizeof(dev_desc_t)) != 0) {
826 ipc_answer_0(callid, EINVAL);
827 ipc_answer_0(iid, EINVAL);
828 return;
829 }
830
831 fibril_mutex_lock(&devices_list_mutex);
832
833 devmap_namespace_t *namespace = devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
834 if (namespace == NULL) {
835 fibril_mutex_unlock(&devices_list_mutex);
836 ipc_answer_0(callid, ENOENT);
837 ipc_answer_0(iid, ENOENT);
838 return;
839 }
840
841 size_t count = size / sizeof(dev_desc_t);
842 if (count != namespace->refcnt) {
843 ipc_answer_0(callid, EOVERFLOW);
844 ipc_answer_0(iid, EOVERFLOW);
845 return;
846 }
847
848 dev_desc_t *desc = (dev_desc_t *) malloc(size);
849 if (desc == NULL) {
850 ipc_answer_0(callid, ENOMEM);
851 ipc_answer_0(iid, EREFUSED);
852 return;
853 }
854
855 link_t *item = devices_list.next;
856 size_t pos = 0;
857 while (item != &devices_list) {
858 devmap_device_t *device = list_get_instance(item, devmap_device_t, devices);
859
860 if (device->namespace == namespace) {
861 desc[pos].handle = device->handle;
862 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, device->name);
863 pos++;
864 }
865
866 item = item->next;
867 }
868
869 ipcarg_t retval = async_data_read_finalize(callid, desc, size);
870
871 free(desc);
872 fibril_mutex_unlock(&devices_list_mutex);
873
874 ipc_answer_0(iid, retval);
875}
876
877static void devmap_null_create(ipc_callid_t iid, ipc_call_t *icall)
878{
879 fibril_mutex_lock(&null_devices_mutex);
880
881 unsigned int i;
882 bool fnd = false;
883
884 for (i = 0; i < NULL_DEVICES; i++) {
885 if (null_devices[i] == NULL) {
886 fnd = true;
887 break;
888 }
889 }
890
891 if (!fnd) {
892 fibril_mutex_unlock(&null_devices_mutex);
893 ipc_answer_0(iid, ENOMEM);
894 return;
895 }
896
897 char null[DEVMAP_NAME_MAXLEN];
898 snprintf(null, DEVMAP_NAME_MAXLEN, "%u", i);
899
900 char *dev_name = str_dup(null);
901 if (dev_name == NULL) {
902 fibril_mutex_unlock(&null_devices_mutex);
903 ipc_answer_0(iid, ENOMEM);
904 return;
905 }
906
907 devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
908 if (device == NULL) {
909 fibril_mutex_unlock(&null_devices_mutex);
910 ipc_answer_0(iid, ENOMEM);
911 return;
912 }
913
914 fibril_mutex_lock(&devices_list_mutex);
915
916 devmap_namespace_t *namespace = devmap_namespace_create("null");
917 if (!namespace) {
918 fibril_mutex_lock(&devices_list_mutex);
919 fibril_mutex_unlock(&null_devices_mutex);
920 ipc_answer_0(iid, ENOMEM);
921 return;
922 }
923
924 list_initialize(&(device->devices));
925 list_initialize(&(device->driver_devices));
926
927 /* Get unique device handle */
928 device->handle = devmap_create_handle();
929 device->driver = NULL;
930
931 devmap_namespace_addref(namespace, device);
932 device->name = dev_name;
933
934 /* Insert device into list of all devices
935 and into null devices array */
936 list_append(&device->devices, &devices_list);
937 null_devices[i] = device;
938
939 fibril_mutex_unlock(&devices_list_mutex);
940 fibril_mutex_unlock(&null_devices_mutex);
941
942 ipc_answer_1(iid, EOK, (ipcarg_t) i);
943}
944
945static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
946{
947 fibril_mutex_lock(&null_devices_mutex);
948
949 ipcarg_t i = IPC_GET_ARG1(*icall);
950
951 if (null_devices[i] == NULL) {
952 ipc_answer_0(iid, ENOENT);
953 return;
954 }
955
956 fibril_mutex_lock(&devices_list_mutex);
957 devmap_device_unregister_core(null_devices[i]);
958 fibril_mutex_unlock(&devices_list_mutex);
959
960 null_devices[i] = NULL;
961
962 fibril_mutex_unlock(&null_devices_mutex);
963
964 ipc_answer_0(iid, EOK);
965}
966
967/** Initialize device mapper.
968 *
969 *
970 */
971static bool devmap_init(void)
972{
973 fibril_mutex_lock(&null_devices_mutex);
974
975 unsigned int i;
976 for (i = 0; i < NULL_DEVICES; i++)
977 null_devices[i] = NULL;
978
979 fibril_mutex_unlock(&null_devices_mutex);
980
981 return true;
982}
983
984/** Handle connection with device driver.
985 *
986 */
987static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
988{
989 /* Accept connection */
990 ipc_answer_0(iid, EOK);
991
992 devmap_driver_t *driver = devmap_driver_register();
993 if (driver == NULL)
994 return;
995
996 bool cont = true;
997 while (cont) {
998 ipc_call_t call;
999 ipc_callid_t callid = async_get_call(&call);
1000
1001 switch (IPC_GET_METHOD(call)) {
1002 case IPC_M_PHONE_HUNGUP:
1003 cont = false;
1004 continue;
1005 case DEVMAP_DRIVER_UNREGISTER:
1006 if (NULL == driver)
1007 ipc_answer_0(callid, ENOENT);
1008 else
1009 ipc_answer_0(callid, EOK);
1010 break;
1011 case DEVMAP_DEVICE_REGISTER:
1012 /* Register one instance of device */
1013 devmap_device_register(callid, &call, driver);
1014 break;
1015 case DEVMAP_DEVICE_UNREGISTER:
1016 /* Remove instance of device identified by handler */
1017 devmap_device_unregister(callid, &call, driver);
1018 break;
1019 case DEVMAP_DEVICE_GET_HANDLE:
1020 devmap_device_get_handle(callid, &call);
1021 break;
1022 case DEVMAP_NAMESPACE_GET_HANDLE:
1023 devmap_namespace_get_handle(callid, &call);
1024 break;
1025 default:
1026 if (!(callid & IPC_CALLID_NOTIFICATION))
1027 ipc_answer_0(callid, ENOENT);
1028 }
1029 }
1030
1031 if (driver != NULL) {
1032 /*
1033 * Unregister the device driver and all its devices.
1034 */
1035 devmap_driver_unregister(driver);
1036 driver = NULL;
1037 }
1038}
1039
1040/** Handle connection with device client.
1041 *
1042 */
1043static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
1044{
1045 /* Accept connection */
1046 ipc_answer_0(iid, EOK);
1047
1048 bool cont = true;
1049 while (cont) {
1050 ipc_call_t call;
1051 ipc_callid_t callid = async_get_call(&call);
1052
1053 switch (IPC_GET_METHOD(call)) {
1054 case IPC_M_PHONE_HUNGUP:
1055 cont = false;
1056 continue;
1057 case DEVMAP_DEVICE_GET_HANDLE:
1058 devmap_device_get_handle(callid, &call);
1059 break;
1060 case DEVMAP_NAMESPACE_GET_HANDLE:
1061 devmap_namespace_get_handle(callid, &call);
1062 break;
1063 case DEVMAP_HANDLE_PROBE:
1064 devmap_handle_probe(callid, &call);
1065 break;
1066 case DEVMAP_NULL_CREATE:
1067 devmap_null_create(callid, &call);
1068 break;
1069 case DEVMAP_NULL_DESTROY:
1070 devmap_null_destroy(callid, &call);
1071 break;
1072 case DEVMAP_GET_NAMESPACE_COUNT:
1073 devmap_get_namespace_count(callid, &call);
1074 break;
1075 case DEVMAP_GET_DEVICE_COUNT:
1076 devmap_get_device_count(callid, &call);
1077 break;
1078 case DEVMAP_GET_NAMESPACES:
1079 devmap_get_namespaces(callid, &call);
1080 break;
1081 case DEVMAP_GET_DEVICES:
1082 devmap_get_devices(callid, &call);
1083 break;
1084 default:
1085 if (!(callid & IPC_CALLID_NOTIFICATION))
1086 ipc_answer_0(callid, ENOENT);
1087 }
1088 }
1089}
1090
1091/** Function for handling connections to devmap
1092 *
1093 */
1094static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
1095{
1096 /* Select interface */
1097 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
1098 case DEVMAP_DRIVER:
1099 devmap_connection_driver(iid, icall);
1100 break;
1101 case DEVMAP_CLIENT:
1102 devmap_connection_client(iid, icall);
1103 break;
1104 case DEVMAP_CONNECT_TO_DEVICE:
1105 /* Connect client to selected device */
1106 devmap_forward(iid, icall);
1107 break;
1108 default:
1109 /* No such interface */
1110 ipc_answer_0(iid, ENOENT);
1111 }
1112}
1113
1114/**
1115 *
1116 */
1117int main(int argc, char *argv[])
1118{
1119 printf(NAME ": HelenOS Device Mapper\n");
1120
1121 if (!devmap_init()) {
1122 printf(NAME ": Error while initializing service\n");
1123 return -1;
1124 }
1125
1126 /* Set a handler of incomming connections */
1127 async_set_client_connection(devmap_connection);
1128
1129 /* Register device mapper at naming service */
1130 ipcarg_t phonead;
1131 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
1132 return -1;
1133
1134 printf(NAME ": Accepting connections\n");
1135 async_manager();
1136
1137 /* Never reached */
1138 return 0;
1139}
1140
1141/**
1142 * @}
1143 */
Note: See TracBrowser for help on using the repository browser.