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

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

improve devmap interface
remove spared device

  • Property mode set to 100644
File size: 18.4 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 <futex.h>
45#include <stdlib.h>
46#include <string.h>
47#include <ipc/devmap.h>
48
49#define NAME "devmap"
50
51/** Representation of device driver.
52 *
53 * Each driver is responsible for a set of devices.
54 *
55 */
56typedef struct {
57 /** Pointers to previous and next drivers in linked list */
58 link_t drivers;
59 /** Pointer to the linked list of devices controlled by this driver */
60 link_t devices;
61 /** Phone asociated with this driver */
62 ipcarg_t phone;
63 /** Device driver name */
64 char *name;
65 /** Futex for list of devices owned by this driver */
66 atomic_t devices_futex;
67} devmap_driver_t;
68
69/** Info about registered device
70 *
71 */
72typedef struct {
73 /** Pointer to the previous and next device in the list of all devices */
74 link_t devices;
75 /** Pointer to the previous and next device in the list of devices
76 owned by one driver */
77 link_t driver_devices;
78 /** Unique device identifier */
79 dev_handle_t handle;
80 /** Device name */
81 char *name;
82 /** Device driver handling this device */
83 devmap_driver_t *driver;
84} devmap_device_t;
85
86/** Pending lookup structure. */
87typedef struct {
88 link_t link;
89 char *name; /**< Device name */
90 ipc_callid_t callid; /**< Call ID waiting for the lookup */
91} pending_req_t;
92
93LIST_INITIALIZE(devices_list);
94LIST_INITIALIZE(drivers_list);
95LIST_INITIALIZE(pending_req);
96
97/* Locking order:
98 * drivers_list_futex
99 * devices_list_futex
100 * (devmap_driver_t *)->devices_futex
101 * create_handle_futex
102 **/
103
104static atomic_t devices_list_futex = FUTEX_INITIALIZER;
105static atomic_t drivers_list_futex = FUTEX_INITIALIZER;
106static atomic_t create_handle_futex = FUTEX_INITIALIZER;
107
108static dev_handle_t last_handle = 0;
109
110static dev_handle_t devmap_create_handle(void)
111{
112 /* TODO: allow reusing old handles after their unregistration
113 * and implement some version of LRU algorithm
114 */
115
116 /* FIXME: overflow */
117 futex_down(&create_handle_futex);
118 last_handle++;
119 futex_up(&create_handle_futex);
120
121 return last_handle;
122}
123
124/** Find device with given name.
125 *
126 */
127static devmap_device_t *devmap_device_find_name(const char *name)
128{
129 link_t *item = devices_list.next;
130 devmap_device_t *device = NULL;
131
132 while (item != &devices_list) {
133 device = list_get_instance(item, devmap_device_t, devices);
134 if (0 == str_cmp(device->name, name))
135 break;
136 item = item->next;
137 }
138
139 if (item == &devices_list)
140 return NULL;
141
142 device = list_get_instance(item, devmap_device_t, devices);
143 return device;
144}
145
146/** Find device with given handle.
147 *
148 * @todo: use hash table
149 *
150 */
151static devmap_device_t *devmap_device_find_handle(dev_handle_t handle)
152{
153 futex_down(&devices_list_futex);
154
155 link_t *item = (&devices_list)->next;
156 devmap_device_t *device = NULL;
157
158 while (item != &devices_list) {
159 device = list_get_instance(item, devmap_device_t, devices);
160 if (device->handle == handle)
161 break;
162 item = item->next;
163 }
164
165 if (item == &devices_list) {
166 futex_up(&devices_list_futex);
167 return NULL;
168 }
169
170 device = list_get_instance(item, devmap_device_t, devices);
171
172 futex_up(&devices_list_futex);
173
174 return device;
175}
176
177/**
178 *
179 * Unregister device and free it. It's assumed that driver's device list is
180 * already locked.
181 *
182 */
183static int devmap_device_unregister_core(devmap_device_t *device)
184{
185 list_remove(&(device->devices));
186 list_remove(&(device->driver_devices));
187
188 free(device->name);
189 free(device);
190
191 return EOK;
192}
193
194/**
195 *
196 * Read info about new driver and add it into linked list of registered
197 * drivers.
198 *
199 */
200static void devmap_driver_register(devmap_driver_t **odriver)
201{
202 *odriver = NULL;
203
204 ipc_call_t icall;
205 ipc_callid_t iid = async_get_call(&icall);
206
207 if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
208 ipc_answer_0(iid, EREFUSED);
209 return;
210 }
211
212 devmap_driver_t *driver = (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
213
214 if (driver == NULL) {
215 ipc_answer_0(iid, ENOMEM);
216 return;
217 }
218
219 /*
220 * Get driver name
221 */
222 ipc_callid_t callid;
223 size_t name_size;
224 if (!ipc_data_write_receive(&callid, &name_size)) {
225 free(driver);
226 ipc_answer_0(callid, EREFUSED);
227 ipc_answer_0(iid, EREFUSED);
228 return;
229 }
230
231 if (name_size > DEVMAP_NAME_MAXLEN) {
232 free(driver);
233 ipc_answer_0(callid, EINVAL);
234 ipc_answer_0(iid, EREFUSED);
235 return;
236 }
237
238 /*
239 * Allocate buffer for device name.
240 */
241 driver->name = (char *) malloc(name_size + 1);
242 if (driver->name == NULL) {
243 free(driver);
244 ipc_answer_0(callid, ENOMEM);
245 ipc_answer_0(iid, EREFUSED);
246 return;
247 }
248
249 /*
250 * Send confirmation to sender and get data into buffer.
251 */
252 if (EOK != ipc_data_write_finalize(callid, driver->name, name_size)) {
253 free(driver->name);
254 free(driver);
255 ipc_answer_0(iid, EREFUSED);
256 return;
257 }
258
259 driver->name[name_size] = 0;
260
261 /* Initialize futex for list of devices owned by this driver */
262 futex_initialize(&(driver->devices_futex), 1);
263
264 /*
265 * Initialize list of asociated devices
266 */
267 list_initialize(&(driver->devices));
268
269 /*
270 * Create connection to the driver
271 */
272 ipc_call_t call;
273 callid = async_get_call(&call);
274
275 if (IPC_M_CONNECT_TO_ME != IPC_GET_METHOD(call)) {
276 ipc_answer_0(callid, ENOTSUP);
277
278 free(driver->name);
279 free(driver);
280 ipc_answer_0(iid, ENOTSUP);
281 return;
282 }
283
284 driver->phone = IPC_GET_ARG5(call);
285
286 ipc_answer_0(callid, EOK);
287
288 list_initialize(&(driver->drivers));
289
290 futex_down(&drivers_list_futex);
291
292 /* TODO:
293 * check that no driver with name equal to driver->name is registered
294 */
295
296 /*
297 * Insert new driver into list of registered drivers
298 */
299 list_append(&(driver->drivers), &drivers_list);
300 futex_up(&drivers_list_futex);
301
302 ipc_answer_0(iid, EOK);
303
304 *odriver = driver;
305}
306
307/**
308 * Unregister device driver, unregister all its devices and free driver
309 * structure.
310 *
311 */
312static int devmap_driver_unregister(devmap_driver_t *driver)
313{
314 if (driver == NULL)
315 return EEXISTS;
316
317 futex_down(&drivers_list_futex);
318
319 if (driver->phone != 0)
320 ipc_hangup(driver->phone);
321
322 /* Remove it from list of drivers */
323 list_remove(&(driver->drivers));
324
325 /* Unregister all its devices */
326 futex_down(&devices_list_futex);
327 futex_down(&(driver->devices_futex));
328
329 while (!list_empty(&(driver->devices))) {
330 devmap_device_t *device = list_get_instance(driver->devices.next,
331 devmap_device_t, driver_devices);
332 devmap_device_unregister_core(device);
333 }
334
335 futex_up(&(driver->devices_futex));
336 futex_up(&devices_list_futex);
337 futex_up(&drivers_list_futex);
338
339 /* free name and driver */
340 if (driver->name != NULL)
341 free(driver->name);
342
343 free(driver);
344
345 return EOK;
346}
347
348
349/** Process pending lookup requests */
350static void process_pending_lookup()
351{
352 link_t *cur;
353
354loop:
355 for (cur = pending_req.next; cur != &pending_req; cur = cur->next) {
356 pending_req_t *pr = list_get_instance(cur, pending_req_t, link);
357
358 const devmap_device_t *dev = devmap_device_find_name(pr->name);
359 if (!dev)
360 continue;
361
362 ipc_answer_1(pr->callid, EOK, dev->handle);
363
364 free(pr->name);
365 list_remove(cur);
366 free(pr);
367 goto loop;
368 }
369}
370
371
372/** Register instance of device
373 *
374 */
375static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
376 devmap_driver_t *driver)
377{
378 if (driver == NULL) {
379 ipc_answer_0(iid, EREFUSED);
380 return;
381 }
382
383 /* Create new device entry */
384 devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
385 if (device == NULL) {
386 ipc_answer_0(iid, ENOMEM);
387 return;
388 }
389
390 /* Get device name */
391 ipc_callid_t callid;
392 size_t size;
393 if (!ipc_data_write_receive(&callid, &size)) {
394 free(device);
395 ipc_answer_0(iid, EREFUSED);
396 return;
397 }
398
399 if (size > DEVMAP_NAME_MAXLEN) {
400 free(device);
401 ipc_answer_0(callid, EINVAL);
402 ipc_answer_0(iid, EREFUSED);
403 return;
404 }
405
406 /* +1 for terminating \0 */
407 device->name = (char *) malloc(size + 1);
408
409 if (device->name == NULL) {
410 free(device);
411 ipc_answer_0(callid, ENOMEM);
412 ipc_answer_0(iid, EREFUSED);
413 return;
414 }
415
416 ipc_data_write_finalize(callid, device->name, size);
417 device->name[size] = 0;
418
419 list_initialize(&(device->devices));
420 list_initialize(&(device->driver_devices));
421
422 futex_down(&devices_list_futex);
423
424 /* Check that device with such name is not already registered */
425 if (NULL != devmap_device_find_name(device->name)) {
426 printf(NAME ": Device '%s' already registered\n", device->name);
427 futex_up(&devices_list_futex);
428 free(device->name);
429 free(device);
430 ipc_answer_0(iid, EEXISTS);
431 return;
432 }
433
434 /* Get unique device handle */
435 device->handle = devmap_create_handle();
436
437 device->driver = driver;
438
439 /* Insert device into list of all devices */
440 list_append(&device->devices, &devices_list);
441
442 /* Insert device into list of devices that belog to one driver */
443 futex_down(&device->driver->devices_futex);
444
445 list_append(&device->driver_devices, &device->driver->devices);
446
447 futex_up(&device->driver->devices_futex);
448 futex_up(&devices_list_futex);
449
450 ipc_answer_1(iid, EOK, device->handle);
451
452 process_pending_lookup();
453}
454
455/**
456 *
457 */
458static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
459 devmap_driver_t *driver)
460{
461 /* TODO */
462 return EOK;
463}
464
465/** Connect client to the device.
466 *
467 * Find device driver owning requested device and forward
468 * the message to it.
469 *
470 */
471static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
472{
473 /*
474 * Get handle from request
475 */
476 dev_handle_t handle = IPC_GET_ARG2(*call);
477 devmap_device_t *dev = devmap_device_find_handle(handle);
478
479 if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) {
480 ipc_answer_0(callid, ENOENT);
481 return;
482 }
483
484 ipc_forward_fast(callid, dev->driver->phone, dev->handle,
485 IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
486}
487
488/** Find handle for device instance identified by name.
489 *
490 * In answer will be send EOK and device handle in arg1 or a error
491 * code from errno.h.
492 *
493 */
494static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
495{
496 /*
497 * Wait for incoming message with device name (but do not
498 * read the name itself until the buffer is allocated).
499 */
500 ipc_callid_t callid;
501 size_t size;
502 if (!ipc_data_write_receive(&callid, &size)) {
503 ipc_answer_0(callid, EREFUSED);
504 ipc_answer_0(iid, EREFUSED);
505 return;
506 }
507
508 if ((size < 1) || (size > DEVMAP_NAME_MAXLEN)) {
509 ipc_answer_0(callid, EINVAL);
510 ipc_answer_0(iid, EREFUSED);
511 return;
512 }
513
514 /*
515 * Allocate buffer for device name.
516 */
517 char *name = (char *) malloc(size + 1);
518 if (name == NULL) {
519 ipc_answer_0(callid, ENOMEM);
520 ipc_answer_0(iid, EREFUSED);
521 return;
522 }
523
524 /*
525 * Send confirmation to sender and get data into buffer.
526 */
527 ipcarg_t retval = ipc_data_write_finalize(callid, name, size);
528 if (retval != EOK) {
529 ipc_answer_0(iid, EREFUSED);
530 free(name);
531 return;
532 }
533 name[size] = '\0';
534
535 /*
536 * Find device name in linked list of known devices.
537 */
538 const devmap_device_t *dev = devmap_device_find_name(name);
539
540 /*
541 * Device was not found.
542 */
543 if (dev == NULL) {
544 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
545 /* Blocking lookup, add to pending list */
546 pending_req_t *pr = (pending_req_t *) malloc(sizeof(pending_req_t));
547 if (!pr) {
548 ipc_answer_0(iid, ENOMEM);
549 free(name);
550 return;
551 }
552
553 pr->name = name;
554 pr->callid = iid;
555 list_append(&pr->link, &pending_req);
556 return;
557 }
558
559 ipc_answer_0(iid, ENOENT);
560 free(name);
561 return;
562 }
563
564 ipc_answer_1(iid, EOK, dev->handle);
565 free(name);
566}
567
568/** Find name of device identified by id and send it to caller.
569 *
570 */
571static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
572{
573 const devmap_device_t *device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
574
575 /*
576 * Device not found.
577 */
578 if (device == NULL) {
579 ipc_answer_0(iid, ENOENT);
580 return;
581 }
582
583 ipc_answer_0(iid, EOK);
584
585 size_t name_size = str_size(device->name);
586
587 /* FIXME:
588 * We have no channel from DEVMAP to client, therefore
589 * sending must be initiated by client.
590 *
591 * int rc = ipc_data_write_send(phone, device->name, name_size);
592 * if (rc != EOK) {
593 * async_wait_for(req, NULL);
594 * return rc;
595 * }
596 */
597
598 /* TODO: send name in response */
599}
600
601static void devmap_get_count(ipc_callid_t iid, ipc_call_t *icall)
602{
603 futex_down(&devices_list_futex);
604 ipc_answer_1(iid, EOK, list_count(&devices_list));
605 futex_up(&devices_list_futex);
606}
607
608static void devmap_get_devices(ipc_callid_t iid, ipc_call_t *icall)
609{
610 futex_down(&devices_list_futex);
611
612 ipc_callid_t callid;
613 size_t size;
614 if (!ipc_data_read_receive(&callid, &size)) {
615 ipc_answer_0(callid, EREFUSED);
616 ipc_answer_0(iid, EREFUSED);
617 return;
618 }
619
620 if ((size % sizeof(dev_desc_t)) != 0) {
621 ipc_answer_0(callid, EINVAL);
622 ipc_answer_0(iid, EREFUSED);
623 return;
624 }
625
626 count_t count = size / sizeof(dev_desc_t);
627 dev_desc_t *desc = (dev_desc_t *) malloc(size);
628 if (desc == NULL) {
629 ipc_answer_0(callid, ENOMEM);
630 ipc_answer_0(iid, EREFUSED);
631 return;
632 }
633
634 count_t pos = 0;
635 link_t *item = devices_list.next;
636
637 while ((item != &devices_list) && (pos < count)) {
638 devmap_device_t *device = list_get_instance(item, devmap_device_t, devices);
639
640 desc[pos].handle = device->handle;
641 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, device->name);
642 pos++;
643 item = item->next;
644 }
645
646 ipcarg_t retval = ipc_data_read_finalize(callid, desc, pos * sizeof(dev_desc_t));
647 if (retval != EOK) {
648 ipc_answer_0(iid, EREFUSED);
649 free(desc);
650 return;
651 }
652
653 free(desc);
654
655 futex_up(&devices_list_futex);
656
657 ipc_answer_1(iid, EOK, pos);
658}
659
660/** Initialize device mapper.
661 *
662 *
663 */
664static bool devmap_init()
665{
666 /* Create NULL device entry */
667 devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
668 if (device == NULL)
669 return false;
670
671 device->name = str_dup("null");
672 if (device->name == NULL) {
673 free(device);
674 return false;
675 }
676
677 list_initialize(&(device->devices));
678 list_initialize(&(device->driver_devices));
679
680 futex_down(&devices_list_futex);
681
682 /* Get unique device handle */
683 device->handle = devmap_create_handle();
684 device->driver = NULL;
685
686 /* Insert device into list of all devices */
687 list_append(&device->devices, &devices_list);
688
689 futex_up(&devices_list_futex);
690
691 return true;
692}
693
694/** Handle connection with device driver.
695 *
696 */
697static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
698{
699 /* Accept connection */
700 ipc_answer_0(iid, EOK);
701
702 devmap_driver_t *driver = NULL;
703 devmap_driver_register(&driver);
704
705 if (NULL == driver)
706 return;
707
708 bool cont = true;
709 while (cont) {
710 ipc_call_t call;
711 ipc_callid_t callid = async_get_call(&call);
712
713 switch (IPC_GET_METHOD(call)) {
714 case IPC_M_PHONE_HUNGUP:
715 cont = false;
716 /* Exit thread */
717 continue;
718 case DEVMAP_DRIVER_UNREGISTER:
719 if (NULL == driver)
720 ipc_answer_0(callid, ENOENT);
721 else
722 ipc_answer_0(callid, EOK);
723 break;
724 case DEVMAP_DEVICE_REGISTER:
725 /* Register one instance of device */
726 devmap_device_register(callid, &call, driver);
727 break;
728 case DEVMAP_DEVICE_UNREGISTER:
729 /* Remove instance of device identified by handler */
730 devmap_device_unregister(callid, &call, driver);
731 break;
732 case DEVMAP_DEVICE_GET_HANDLE:
733 devmap_get_handle(callid, &call);
734 break;
735 case DEVMAP_DEVICE_GET_NAME:
736 devmap_get_name(callid, &call);
737 break;
738 default:
739 if (!(callid & IPC_CALLID_NOTIFICATION))
740 ipc_answer_0(callid, ENOENT);
741 }
742 }
743
744 if (NULL != driver) {
745 /*
746 * Unregister the device driver and all its devices.
747 */
748 devmap_driver_unregister(driver);
749 driver = NULL;
750 }
751}
752
753/** Handle connection with device client.
754 *
755 */
756static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
757{
758 /* Accept connection */
759 ipc_answer_0(iid, EOK);
760
761 bool cont = true;
762 while (cont) {
763 ipc_call_t call;
764 ipc_callid_t callid = async_get_call(&call);
765
766 switch (IPC_GET_METHOD(call)) {
767 case IPC_M_PHONE_HUNGUP:
768 cont = false;
769 /* Exit thread */
770 continue;
771 case DEVMAP_DEVICE_GET_HANDLE:
772 devmap_get_handle(callid, &call);
773 break;
774 case DEVMAP_DEVICE_GET_NAME:
775 devmap_get_name(callid, &call);
776 break;
777 case DEVMAP_DEVICE_GET_COUNT:
778 devmap_get_count(callid, &call);
779 break;
780 case DEVMAP_DEVICE_GET_DEVICES:
781 devmap_get_devices(callid, &call);
782 break;
783 default:
784 if (!(callid & IPC_CALLID_NOTIFICATION))
785 ipc_answer_0(callid, ENOENT);
786 }
787 }
788}
789
790/** Function for handling connections to devmap
791 *
792 */
793static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
794{
795 /* Select interface */
796 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
797 case DEVMAP_DRIVER:
798 devmap_connection_driver(iid, icall);
799 break;
800 case DEVMAP_CLIENT:
801 devmap_connection_client(iid, icall);
802 break;
803 case DEVMAP_CONNECT_TO_DEVICE:
804 /* Connect client to selected device */
805 devmap_forward(iid, icall);
806 break;
807 default:
808 /* No such interface */
809 ipc_answer_0(iid, ENOENT);
810 }
811}
812
813/**
814 *
815 */
816int main(int argc, char *argv[])
817{
818 printf(NAME ": HelenOS Device Mapper\n");
819
820 if (!devmap_init()) {
821 printf(NAME ": Error while initializing service\n");
822 return -1;
823 }
824
825 /* Set a handler of incomming connections */
826 async_set_client_connection(devmap_connection);
827
828 /* Register device mapper at naming service */
829 ipcarg_t phonead;
830 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
831 return -1;
832
833 printf(NAME ": Accepting connections\n");
834 async_manager();
835
836 /* Never reached */
837 return 0;
838}
839
840/**
841 * @}
842 */
Note: See TracBrowser for help on using the repository browser.