source: mainline/uspace/srv/devmap/devmap.c@ 24345a5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 24345a5 was 21c5d41, checked in by Martin Decky <martin@…>, 17 years ago

remove extensive debugging output
remove private devmap.h

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