source: mainline/uspace/srv/devmap/devmap.c@ 595edf5

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

remove futex (devmap is not multithreaded), use fibril serialization instead

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