source: mainline/uspace/srv/devmap/devmap.c@ 00fe6bb

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

Do not use the pending mechanism in devmap.

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