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

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

Add mode argument to IPC forward.
This argument can be used to modify the way forward behaves.

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