source: mainline/uspace/srv/devmap/devmap.c@ 7f3e3e7

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

Cstyle fixes.

  • Property mode set to 100644
File size: 16.4 KB
RevLine 
[13125d3]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>
[07e4a3c]43#include <bool.h>
44#include <futex.h>
[798f364]45#include <stdlib.h>
46#include <string.h>
[13125d3]47
48#include "devmap.h"
49
[07e4a3c]50
[798f364]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
[07e4a3c]84
[13125d3]85/** Initialize device mapper.
86 *
87 *
88 */
89static int devmap_init()
90{
[798f364]91 /* TODO: */
[13125d3]92
[07e4a3c]93 return EOK;
[13125d3]94}
95
[798f364]96/** Find device with given name.
97 *
98 */
99static devmap_device_t *devmap_device_find_name(const char *name)
[07e4a3c]100{
[798f364]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
[7f3e3e7]157/**
158 * Unregister device and free it. It's assumed that driver's device list is
159 * already locked.
[798f364]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
[7f3e3e7]174/**
175 * Read info about new driver and add it into linked list of registered
176 * drivers.
[798f364]177 */
178static void devmap_driver_register(devmap_driver_t **odriver)
179{
180 size_t name_size;
[07e4a3c]181 ipc_callid_t callid;
182 ipc_call_t call;
[798f364]183 devmap_driver_t *driver;
184 ipc_callid_t iid;
185 ipc_call_t icall;
186
187 *odriver = NULL;
[07e4a3c]188
[798f364]189 iid = async_get_call(&icall);
190
191 if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
192 ipc_answer_fast(iid, EREFUSED, 0, 0);
193 return;
194 }
195
[7f3e3e7]196 if (NULL ==
197 (driver = (devmap_driver_t *)malloc(sizeof(devmap_driver_t)))) {
[798f364]198 ipc_answer_fast(iid, ENOMEM, 0, 0);
199 return;
[07e4a3c]200 }
201
[798f364]202 /*
203 * Get driver name
204 */
205 if (!ipc_data_receive(&callid, &call, NULL, &name_size)) {
206 printf("Unexpected request: %u.\n", IPC_GET_METHOD(call));
207 free(driver);
208 ipc_answer_fast(callid, EREFUSED, 0, 0);
209 ipc_answer_fast(iid, EREFUSED, 0, 0);
210 return;
[07e4a3c]211 }
[798f364]212
213 if (name_size > DEVMAP_NAME_MAXLEN) {
[7f3e3e7]214 printf("Too logn name: %u: maximum is %u.\n", name_size,
215 DEVMAP_NAME_MAXLEN);
[798f364]216 free(driver);
217 ipc_answer_fast(callid, EINVAL, 0, 0);
218 ipc_answer_fast(iid, EREFUSED, 0, 0);
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_fast(callid, ENOMEM, 0, 0);
229 ipc_answer_fast(iid, EREFUSED, 0, 0);
230 return;
231 }
232
233 /*
234 * Send confirmation to sender and get data into buffer.
235 */
236 if (EOK != ipc_data_deliver(callid, &call, driver->name, name_size)) {
237 printf("Cannot read driver name.\n");
238 free(driver->name);
239 free(driver);
240 ipc_answer_fast(iid, EREFUSED, 0, 0);
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 */
[7f3e3e7]249 futex_initialize(&(driver->devices_futex), 1);
[798f364]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)) {
[7f3e3e7]262 printf("DevMap: Unexpected method: %u.\n",
263 IPC_GET_METHOD(call));
[798f364]264 ipc_answer_fast(callid, ENOTSUP, 0, 0);
265
266 free(driver->name);
267 free(driver);
268 ipc_answer_fast(iid, ENOTSUP, 0, 0);
269 return;
270 }
271
272 driver->phone = IPC_GET_ARG3(call);
273
274 ipc_answer_fast(callid, EOK, 0, 0);
275
276 list_initialize(&(driver->drivers));
277
278 futex_down(&drivers_list_futex);
279
[7f3e3e7]280 /* TODO:
281 * check that no driver with name equal to driver->name is registered
282 */
[798f364]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_fast(iid, EOK, 0, 0);
291 printf("Driver registered.\n");
292
293 *odriver = driver;
294 return;
[07e4a3c]295}
296
[798f364]297/** Unregister device driver, unregister all its devices and free driver structure.
298 *
299 */
300static int devmap_driver_unregister(devmap_driver_t *driver)
[07e4a3c]301{
[798f364]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))) {
[7f3e3e7]323 device = list_get_instance(driver->devices.next,
324 devmap_device_t, driver_devices);
[798f364]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
[07e4a3c]342 return EOK;
343}
344
[798f364]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)
[07e4a3c]351{
[798f364]352 ipc_callid_t callid;
353 ipc_call_t call;
354 size_t size;
355 devmap_device_t *device;
[07e4a3c]356
[798f364]357 if (NULL == driver) {
358 printf("Invalid driver registration.\n");
359 ipc_answer_fast(iid, EREFUSED, 0, 0);
360 return;
361 }
362
363 /* Create new device entry */
[7f3e3e7]364 if (NULL ==
365 (device = (devmap_device_t *)malloc(sizeof(devmap_device_t)))) {
[798f364]366 printf("Cannot allocate new device.\n");
367 ipc_answer_fast(iid, ENOMEM, 0, 0);
368 return;
369 }
370
371 /* Get device name */
372 if (!ipc_data_receive(&callid, &call, NULL, &size)) {
373 free(device);
374 printf("Cannot read device name.\n");
375 ipc_answer_fast(iid, EREFUSED, 0, 0);
376 return;
377 }
[07e4a3c]378
[798f364]379 if (size > DEVMAP_NAME_MAXLEN) {
380 printf("Too long device name: %u.\n", size);
381 free(device);
382 ipc_answer_fast(callid, EINVAL, 0, 0);
383 ipc_answer_fast(iid, EREFUSED, 0, 0);
384 return;
385 }
[07e4a3c]386
[798f364]387 /* +1 for terminating \0 */
388 device->name = (char *)malloc(size + 1);
[07e4a3c]389
[798f364]390 if (NULL == device->name) {
391 printf("Cannot read device name.\n");
392 free(device);
393 ipc_answer_fast(callid, ENOMEM, 0, 0);
394 ipc_answer_fast(iid, EREFUSED, 0, 0);
395 return;
[07e4a3c]396 }
[798f364]397
398 ipc_data_deliver(callid, &call, device->name, size);
399 device->name[size] = 0;
400
401 list_initialize(&(device->devices));
402 list_initialize(&(device->driver_devices));
403
404 futex_down(&devices_list_futex);
405
406 /* Check that device with such name is not already registered */
407 if (NULL != devmap_device_find_name(device->name)) {
408 printf("Device '%s' already registered.\n", device->name);
409 futex_up(&devices_list_futex);
410 free(device->name);
411 free(device);
412 ipc_answer_fast(iid, EEXISTS, 0, 0);
413 return;
[07e4a3c]414 }
415
[798f364]416 /* Get unique device handle */
417 device->handle = devmap_create_handle();
[07e4a3c]418
[798f364]419 device->driver = driver;
[07e4a3c]420
[798f364]421 /* Insert device into list of all devices */
422 list_append(&(device->devices), &devices_list);
423
424 /* Insert device into list of devices that belog to one driver */
425 futex_down(&(device->driver->devices_futex));
426
427 list_append(&(device->driver_devices), &(device->driver->devices));
428
429 futex_up(&(device->driver->devices_futex));
430 futex_up(&devices_list_futex);
431
432 printf("Device '%s' registered.\n", device->name);
433 ipc_answer_fast(iid, EOK, device->handle, 0);
434
435 return;
[07e4a3c]436}
437
[798f364]438/**
439 *
440 */
441static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
442 devmap_driver_t *driver)
[07e4a3c]443{
[798f364]444 /* TODO */
445
[07e4a3c]446 return EOK;
447}
[13125d3]448
[798f364]449/** Connect client to the device.
450 * Find device driver owning requested device and forward
451 * the message to it.
452 *
453 *
454 */
455static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
456{
457 devmap_device_t *dev;
458 int handle;
459
460 /*
461 * Get handle from request
462 */
463 handle = IPC_GET_ARG1(*call);
464 dev = devmap_device_find_handle(handle);
465
466 if (NULL == dev) {
[7f3e3e7]467 printf("DevMap: No registered device with handle %d.\n",
468 handle);
[798f364]469 ipc_answer_fast(callid, ENOENT, 0, 0);
470 return;
471 }
472
[7f3e3e7]473 /* FIXME: is this correct method how to pass argument on forwarding ?*/
474 ipc_forward_fast(callid, dev->driver->phone, (ipcarg_t)(dev->handle),
475 0);
[798f364]476 return;
477}
478
479/** Find handle for device instance identified by name.
480 * In answer will be send EOK and device handle in arg1 or a error
481 * code from errno.h.
482 */
483static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
484{
485 char *name = NULL;
486 size_t name_size;
487 const devmap_device_t *dev;
488 ipc_callid_t callid;
489 ipc_call_t call;
490 ipcarg_t retval;
491
492
493 /*
494 * Wait for incoming message with device name (but do not
495 * read the name itself until the buffer is allocated).
496 */
497 if (!ipc_data_receive(&callid, &call, NULL, &name_size)) {
498 ipc_answer_fast(callid, EREFUSED, 0, 0);
499 ipc_answer_fast(iid, EREFUSED, 0, 0);
500 return;
501 }
502
503 if (name_size > DEVMAP_NAME_MAXLEN) {
504 ipc_answer_fast(callid, EINVAL, 0, 0);
505 ipc_answer_fast(iid, EREFUSED, 0, 0);
506 return;
507 }
508
509 /*
510 * Allocate buffer for device name.
511 */
512 if (NULL == (name = (char *)malloc(name_size))) {
513 ipc_answer_fast(callid, ENOMEM, 0, 0);
514 ipc_answer_fast(iid, EREFUSED, 0, 0);
515 return;
516 }
517
518 /*
519 * Send confirmation to sender and get data into buffer.
520 */
[7f3e3e7]521 if (EOK != (retval = ipc_data_deliver(callid, &call, name,
522 name_size))) {
[798f364]523 ipc_answer_fast(iid, EREFUSED, 0, 0);
524 return;
525 }
526
527 /*
528 * Find device name in linked list of known devices.
529 */
530 dev = devmap_device_find_name(name);
531
532 /*
533 * Device was not found.
534 */
535 if (NULL == dev) {
536 printf("DevMap: device %s has not been registered.\n", name);
537 ipc_answer_fast(iid, ENOENT, 0, 0);
538 return;
539 }
540
541 printf("DevMap: device %s has handler %d.\n", name, dev->handle);
542
543 ipc_answer_fast(iid, EOK, dev->handle, 0);
544
545 return;
546}
547
548/** Find name of device identified by id and send it to caller.
549 *
550 */
551static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
552{
553 const devmap_device_t *device;
554 size_t name_size;
555
556 device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
557
558 /*
559 * Device not found.
560 */
561 if (NULL == device) {
562 ipc_answer_fast(iid, ENOENT, 0, 0);
563 return;
564 }
565
566 ipc_answer_fast(iid, EOK, 0, 0);
567
568 name_size = strlen(device->name);
569
570
571/* FIXME:
572 we have no channel from DevMap to client ->
573 sending must be initiated by client
574
575 int rc = ipc_data_send(phone, device->name, name_size);
576 if (rc != EOK) {
577 async_wait_for(req, NULL);
578 return rc;
579 }
580*/
581 /* TODO: send name in response */
582
583 return;
584}
585
586/** Handle connection with device driver.
[13125d3]587 *
588 */
589static void
[798f364]590devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[13125d3]591{
592 ipc_callid_t callid;
593 ipc_call_t call;
[07e4a3c]594 bool cont = true;
[798f364]595 devmap_driver_t *driver = NULL;
[07e4a3c]596
[798f364]597 ipc_answer_fast(iid, EOK, 0, 0);
[13125d3]598
[798f364]599 devmap_driver_register(&driver);
[13125d3]600
[798f364]601 if (NULL == driver) {
602 printf("DevMap: driver registration failed.\n");
603 return;
604 }
605
[07e4a3c]606 while (cont) {
[13125d3]607 callid = async_get_call(&call);
608
609 switch (IPC_GET_METHOD(call)) {
610 case IPC_M_PHONE_HUNGUP:
[798f364]611 printf("DevMap: connection hung up.\n");
[07e4a3c]612 cont = false;
613 continue; /* Exit thread */
[798f364]614 case DEVMAP_DRIVER_UNREGISTER:
615 printf("DevMap: unregister driver.\n");
616 if (NULL == driver) {
617 printf("DevMap: driver was not registered!\n");
618 ipc_answer_fast(callid, ENOENT, 0, 0);
619 } else {
620 ipc_answer_fast(callid, EOK, 0, 0);
[07e4a3c]621 }
622 break;
[798f364]623 case DEVMAP_DEVICE_REGISTER:
624 /* Register one instance of device */
625 devmap_device_register(callid, &call, driver);
[07e4a3c]626 break;
[798f364]627 case DEVMAP_DEVICE_UNREGISTER:
628 /* Remove instance of device identified by handler */
629 devmap_device_unregister(callid, &call, driver);
630 break;
631 case DEVMAP_DEVICE_GET_HANDLE:
632 devmap_get_handle(callid, &call);
633 break;
634 case DEVMAP_DEVICE_GET_NAME:
635 devmap_get_handle(callid, &call);
636 break;
637 default:
638 if (!(callid & IPC_CALLID_NOTIFICATION)) {
639 ipc_answer_fast(callid, ENOENT, 0, 0);
640 }
641 }
642 }
643
644 if (NULL != driver) {
645 /*
646 * Unregister the device driver and all its devices.
647 */
648 devmap_driver_unregister(driver);
649 driver = NULL;
650 }
651
652}
653
654/** Handle connection with device client.
655 *
656 */
657static void
658devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
659{
660 ipc_callid_t callid;
661 ipc_call_t call;
662 bool cont = true;
663
664 ipc_answer_fast(iid, EOK, 0, 0); /* Accept connection */
665
666 while (cont) {
667 callid = async_get_call(&call);
668
669 switch (IPC_GET_METHOD(call)) {
670 case IPC_M_PHONE_HUNGUP:
671 printf("DevMap: connection hung up.\n");
[07e4a3c]672 cont = false;
[798f364]673 continue; /* Exit thread */
674
675 case DEVMAP_DEVICE_CONNECT_ME_TO:
676 /* Connect client to selected device */
[7f3e3e7]677 printf("DevMap: connect to device %d.\n",
678 IPC_GET_ARG1(call));
[798f364]679 devmap_forward(callid, &call);
680 break;
681
682 case DEVMAP_DEVICE_GET_HANDLE:
683 devmap_get_handle(callid, &call);
684
[07e4a3c]685 break;
[798f364]686 case DEVMAP_DEVICE_GET_NAME:
687 /* TODO */
688 devmap_get_name(callid, &call);
[13125d3]689 break;
690 default:
[798f364]691 if (!(callid & IPC_CALLID_NOTIFICATION)) {
692 ipc_answer_fast(callid, ENOENT, 0, 0);
693 }
[13125d3]694 }
695 }
696}
697
[798f364]698/** Function for handling connections to devmap
699 *
700 */
701static void
702devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
703{
704
705 printf("DevMap: new connection.\n");
706
707 /* Select interface */
708 switch ((ipcarg_t)(IPC_GET_ARG1(*icall))) {
[7f3e3e7]709 case DEVMAP_DRIVER:
710 devmap_connection_driver(iid, icall);
711 break;
712 case DEVMAP_CLIENT:
713 devmap_connection_client(iid, icall);
714 break;
715 default:
716 ipc_answer_fast(iid, ENOENT, 0, 0); /* No such interface */
717 printf("DevMap: Unknown interface %u.\n",
718 (ipcarg_t)(IPC_GET_ARG1(*icall)));
[798f364]719 }
720
721 /* Cleanup */
722
723 printf("DevMap: connection closed.\n");
724 return;
725}
[13125d3]726
[798f364]727/**
728 *
729 */
[13125d3]730int main(int argc, char *argv[])
731{
732 ipcarg_t phonead;
733
734 printf("DevMap: HelenOS device mapper.\n");
735
736 if (devmap_init() != 0) {
[798f364]737 printf("Error while initializing DevMap service.\n");
[13125d3]738 return -1;
739 }
740
741 /* Set a handler of incomming connections */
[798f364]742 async_set_client_connection(devmap_connection);
[13125d3]743
[7f3e3e7]744 /* Register device mapper at naming service */
[13125d3]745 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, &phonead) != 0)
746 return -1;
747
748 async_manager();
749 /* Never reached */
750 return 0;
751}
752
753/**
754 * @}
755 */
756
Note: See TracBrowser for help on using the repository browser.