source: mainline/uspace/srv/devmap/devmap.c@ 6364d3c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6364d3c was 38c706cc, checked in by Josef Cejka <malyzelenyhnus@…>, 18 years ago

Extended IPC_M_CONNECT_TO_ME to use 3 user defined parameters.
Phone identifier is passed in ARG5.

  • 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_ARG5(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 */
452static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
453{
454 devmap_device_t *dev;
455 int handle;
456
457 /*
458 * Get handle from request
459 */
460 handle = IPC_GET_ARG2(*call);
461 dev = devmap_device_find_handle(handle);
462
463 if (NULL == dev) {
464 printf("DEVMAP: No registered device with handle %d.\n",
465 handle);
466 ipc_answer_0(callid, ENOENT);
467 return;
468 }
469
470 ipc_forward_fast(callid, dev->driver->phone, (ipcarg_t)(dev->handle),
471 IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
472 return;
473}
474
475/** Find handle for device instance identified by name.
476 * In answer will be send EOK and device handle in arg1 or a error
477 * code from errno.h.
478 */
479static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
480{
481 char *name = NULL;
482 size_t name_size;
483 const devmap_device_t *dev;
484 ipc_callid_t callid;
485 ipcarg_t retval;
486
487
488 /*
489 * Wait for incoming message with device name (but do not
490 * read the name itself until the buffer is allocated).
491 */
492 if (!ipc_data_receive(&callid, NULL, &name_size)) {
493 ipc_answer_0(callid, EREFUSED);
494 ipc_answer_0(iid, EREFUSED);
495 return;
496 }
497
498 if (name_size > DEVMAP_NAME_MAXLEN) {
499 ipc_answer_0(callid, EINVAL);
500 ipc_answer_0(iid, EREFUSED);
501 return;
502 }
503
504 /*
505 * Allocate buffer for device name.
506 */
507 if (NULL == (name = (char *)malloc(name_size))) {
508 ipc_answer_0(callid, ENOMEM);
509 ipc_answer_0(iid, EREFUSED);
510 return;
511 }
512
513 /*
514 * Send confirmation to sender and get data into buffer.
515 */
516 if (EOK != (retval = ipc_data_deliver(callid, name, name_size))) {
517 ipc_answer_0(iid, EREFUSED);
518 return;
519 }
520
521 /*
522 * Find device name in linked list of known devices.
523 */
524 dev = devmap_device_find_name(name);
525
526 /*
527 * Device was not found.
528 */
529 if (NULL == dev) {
530 printf("DEVMAP: device %s has not been registered.\n", name);
531 ipc_answer_0(iid, ENOENT);
532 return;
533 }
534
535 printf("DEVMAP: device %s has handler %d.\n", name, dev->handle);
536
537 ipc_answer_1(iid, EOK, dev->handle);
538
539 return;
540}
541
542/** Find name of device identified by id and send it to caller.
543 *
544 */
545static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
546{
547 const devmap_device_t *device;
548 size_t name_size;
549
550 device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
551
552 /*
553 * Device not found.
554 */
555 if (NULL == device) {
556 ipc_answer_0(iid, ENOENT);
557 return;
558 }
559
560 ipc_answer_0(iid, EOK);
561
562 name_size = strlen(device->name);
563
564
565/* FIXME:
566 we have no channel from DEVMAP to client ->
567 sending must be initiated by client
568
569 int rc = ipc_data_send(phone, device->name, name_size);
570 if (rc != EOK) {
571 async_wait_for(req, NULL);
572 return rc;
573 }
574*/
575 /* TODO: send name in response */
576
577 return;
578}
579
580/** Handle connection with device driver.
581 *
582 */
583static void
584devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
585{
586 ipc_callid_t callid;
587 ipc_call_t call;
588 bool cont = true;
589 devmap_driver_t *driver = NULL;
590
591 ipc_answer_0(iid, EOK);
592
593 devmap_driver_register(&driver);
594
595 if (NULL == driver) {
596 printf("DEVMAP: driver registration failed.\n");
597 return;
598 }
599
600 while (cont) {
601 callid = async_get_call(&call);
602
603 switch (IPC_GET_METHOD(call)) {
604 case IPC_M_PHONE_HUNGUP:
605 printf("DEVMAP: connection hung up.\n");
606 cont = false;
607 continue; /* Exit thread */
608 case DEVMAP_DRIVER_UNREGISTER:
609 printf("DEVMAP: unregister driver.\n");
610 if (NULL == driver) {
611 printf("DEVMAP: driver was not registered!\n");
612 ipc_answer_0(callid, ENOENT);
613 } else {
614 ipc_answer_0(callid, EOK);
615 }
616 break;
617 case DEVMAP_DEVICE_REGISTER:
618 /* Register one instance of device */
619 devmap_device_register(callid, &call, driver);
620 break;
621 case DEVMAP_DEVICE_UNREGISTER:
622 /* Remove instance of device identified by handler */
623 devmap_device_unregister(callid, &call, driver);
624 break;
625 case DEVMAP_DEVICE_GET_HANDLE:
626 devmap_get_handle(callid, &call);
627 break;
628 case DEVMAP_DEVICE_GET_NAME:
629 devmap_get_handle(callid, &call);
630 break;
631 default:
632 if (!(callid & IPC_CALLID_NOTIFICATION)) {
633 ipc_answer_0(callid, ENOENT);
634 }
635 }
636 }
637
638 if (NULL != driver) {
639 /*
640 * Unregister the device driver and all its devices.
641 */
642 devmap_driver_unregister(driver);
643 driver = NULL;
644 }
645
646}
647
648/** Handle connection with device client.
649 *
650 */
651static void
652devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
653{
654 ipc_callid_t callid;
655 ipc_call_t call;
656 bool cont = true;
657
658 ipc_answer_0(iid, EOK); /* Accept connection */
659
660 while (cont) {
661 callid = async_get_call(&call);
662
663 switch (IPC_GET_METHOD(call)) {
664 case IPC_M_PHONE_HUNGUP:
665 printf("DEVMAP: connection hung up.\n");
666 cont = false;
667 continue; /* Exit thread */
668
669 case DEVMAP_DEVICE_GET_HANDLE:
670 devmap_get_handle(callid, &call);
671
672 break;
673 case DEVMAP_DEVICE_GET_NAME:
674 /* TODO */
675 devmap_get_name(callid, &call);
676 break;
677 default:
678 if (!(callid & IPC_CALLID_NOTIFICATION)) {
679 ipc_answer_0(callid, ENOENT);
680 }
681 }
682 }
683}
684
685/** Function for handling connections to devmap
686 *
687 */
688static void
689devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
690{
691
692 printf("DEVMAP: new connection.\n");
693
694 /* Select interface */
695 switch ((ipcarg_t)(IPC_GET_ARG1(*icall))) {
696 case DEVMAP_DRIVER:
697 devmap_connection_driver(iid, icall);
698 break;
699 case DEVMAP_CLIENT:
700 devmap_connection_client(iid, icall);
701 break;
702 case DEVMAP_CONNECT_TO_DEVICE:
703 /* Connect client to selected device */
704 printf("DEVMAP: connect to device %d.\n",
705 IPC_GET_ARG2(*icall));
706 devmap_forward(iid, icall);
707 break;
708 default:
709 ipc_answer_0(iid, ENOENT); /* No such interface */
710 printf("DEVMAP: Unknown interface %u.\n",
711 (ipcarg_t)(IPC_GET_ARG1(*icall)));
712 }
713
714 /* Cleanup */
715
716 printf("DEVMAP: connection closed.\n");
717 return;
718}
719
720/**
721 *
722 */
723int main(int argc, char *argv[])
724{
725 ipcarg_t phonead;
726
727 printf("DEVMAP: HelenOS device mapper.\n");
728
729 if (devmap_init() != 0) {
730 printf("Error while initializing DEVMAP service.\n");
731 return -1;
732 }
733
734 /* Set a handler of incomming connections */
735 async_set_client_connection(devmap_connection);
736
737 /* Register device mapper at naming service */
738 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
739 return -1;
740
741 async_manager();
742 /* Never reached */
743 return 0;
744}
745
746/**
747 * @}
748 */
749
Note: See TracBrowser for help on using the repository browser.