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
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#include <ipc/devmap.h>
48
49#define NAME "devmap"
50
51
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
85
86/** Initialize device mapper.
87 *
88 *
89 */
90static int devmap_init()
91{
92 /* TODO: */
93
94 return EOK;
95}
96
97/** Find device with given name.
98 *
99 */
100static devmap_device_t *devmap_device_find_name(const char *name)
101{
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
116 if (item == &devices_list)
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
156/**
157 * Unregister device and free it. It's assumed that driver's device list is
158 * already locked.
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
173/**
174 * Read info about new driver and add it into linked list of registered
175 * drivers.
176 */
177static void devmap_driver_register(devmap_driver_t **odriver)
178{
179 size_t name_size;
180 ipc_callid_t callid;
181 ipc_call_t call;
182 devmap_driver_t *driver;
183 ipc_callid_t iid;
184 ipc_call_t icall;
185
186 *odriver = NULL;
187
188 iid = async_get_call(&icall);
189
190 if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
191 ipc_answer_0(iid, EREFUSED);
192 return;
193 }
194
195 if (NULL ==
196 (driver = (devmap_driver_t *)malloc(sizeof(devmap_driver_t)))) {
197 ipc_answer_0(iid, ENOMEM);
198 return;
199 }
200
201 /*
202 * Get driver name
203 */
204 if (!ipc_data_write_receive(&callid, &name_size)) {
205 free(driver);
206 ipc_answer_0(callid, EREFUSED);
207 ipc_answer_0(iid, EREFUSED);
208 return;
209 }
210
211 if (name_size > DEVMAP_NAME_MAXLEN) {
212 free(driver);
213 ipc_answer_0(callid, EINVAL);
214 ipc_answer_0(iid, EREFUSED);
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);
223 ipc_answer_0(callid, ENOMEM);
224 ipc_answer_0(iid, EREFUSED);
225 return;
226 }
227
228 /*
229 * Send confirmation to sender and get data into buffer.
230 */
231 if (EOK != ipc_data_write_finalize(callid, driver->name, name_size)) {
232 free(driver->name);
233 free(driver);
234 ipc_answer_0(iid, EREFUSED);
235 return;
236 }
237
238 driver->name[name_size] = 0;
239
240 /* Initialize futex for list of devices owned by this driver */
241 futex_initialize(&(driver->devices_futex), 1);
242
243 /*
244 * Initialize list of asociated devices
245 */
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)) {
254 ipc_answer_0(callid, ENOTSUP);
255
256 free(driver->name);
257 free(driver);
258 ipc_answer_0(iid, ENOTSUP);
259 return;
260 }
261
262 driver->phone = IPC_GET_ARG5(call);
263
264 ipc_answer_0(callid, EOK);
265
266 list_initialize(&(driver->drivers));
267
268 futex_down(&drivers_list_futex);
269
270 /* TODO:
271 * check that no driver with name equal to driver->name is registered
272 */
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
280 ipc_answer_0(iid, EOK);
281
282 *odriver = driver;
283}
284
285/** Unregister device driver, unregister all its devices and free driver
286 * structure.
287 */
288static int devmap_driver_unregister(devmap_driver_t *driver)
289{
290 devmap_device_t *device;
291
292 if (NULL == driver)
293 return EEXISTS;
294
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))) {
308 device = list_get_instance(driver->devices.next,
309 devmap_device_t, driver_devices);
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
324 return EOK;
325}
326
327
328/** Register instance of device
329 *
330 */
331static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
332 devmap_driver_t *driver)
333{
334 ipc_callid_t callid;
335 size_t size;
336 devmap_device_t *device;
337
338 if (NULL == driver) {
339 ipc_answer_0(iid, EREFUSED);
340 return;
341 }
342
343 /* Create new device entry */
344 if (NULL ==
345 (device = (devmap_device_t *) malloc(sizeof(devmap_device_t)))) {
346 ipc_answer_0(iid, ENOMEM);
347 return;
348 }
349
350 /* Get device name */
351 if (!ipc_data_write_receive(&callid, &size)) {
352 free(device);
353 ipc_answer_0(iid, EREFUSED);
354 return;
355 }
356
357 if (size > DEVMAP_NAME_MAXLEN) {
358 free(device);
359 ipc_answer_0(callid, EINVAL);
360 ipc_answer_0(iid, EREFUSED);
361 return;
362 }
363
364 /* +1 for terminating \0 */
365 device->name = (char *) malloc(size + 1);
366
367 if (NULL == device->name) {
368 free(device);
369 ipc_answer_0(callid, ENOMEM);
370 ipc_answer_0(iid, EREFUSED);
371 return;
372 }
373
374 ipc_data_write_finalize(callid, device->name, size);
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)) {
384 printf(NAME ": Device '%s' already registered\n", device->name);
385 futex_up(&devices_list_futex);
386 free(device->name);
387 free(device);
388 ipc_answer_0(iid, EEXISTS);
389 return;
390 }
391
392 /* Get unique device handle */
393 device->handle = devmap_create_handle();
394
395 device->driver = driver;
396
397 /* Insert device into list of all devices */
398 list_append(&device->devices, &devices_list);
399
400 /* Insert device into list of devices that belog to one driver */
401 futex_down(&device->driver->devices_futex);
402
403 list_append(&device->driver_devices, &device->driver->devices);
404
405 futex_up(&device->driver->devices_futex);
406 futex_up(&devices_list_futex);
407
408 ipc_answer_1(iid, EOK, device->handle);
409}
410
411/**
412 *
413 */
414static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
415 devmap_driver_t *driver)
416{
417 /* TODO */
418
419 return EOK;
420}
421
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 */
434 handle = IPC_GET_ARG2(*call);
435 dev = devmap_device_find_handle(handle);
436
437 if (NULL == dev) {
438 ipc_answer_0(callid, ENOENT);
439 return;
440 }
441
442 ipc_forward_fast(callid, dev->driver->phone, (ipcarg_t)(dev->handle),
443 IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
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 */
462 if (!ipc_data_write_receive(&callid, &name_size)) {
463 ipc_answer_0(callid, EREFUSED);
464 ipc_answer_0(iid, EREFUSED);
465 return;
466 }
467
468 if (name_size > DEVMAP_NAME_MAXLEN) {
469 ipc_answer_0(callid, EINVAL);
470 ipc_answer_0(iid, EREFUSED);
471 return;
472 }
473
474 /*
475 * Allocate buffer for device name.
476 */
477 if (NULL == (name = (char *)malloc(name_size))) {
478 ipc_answer_0(callid, ENOMEM);
479 ipc_answer_0(iid, EREFUSED);
480 return;
481 }
482
483 /*
484 * Send confirmation to sender and get data into buffer.
485 */
486 if (EOK != (retval = ipc_data_write_finalize(callid, name,
487 name_size))) {
488 ipc_answer_0(iid, EREFUSED);
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) {
501 ipc_answer_0(iid, ENOENT);
502 return;
503 }
504
505 ipc_answer_1(iid, EOK, dev->handle);
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) {
522 ipc_answer_0(iid, ENOENT);
523 return;
524 }
525
526 ipc_answer_0(iid, EOK);
527
528 name_size = strlen(device->name);
529
530
531/* FIXME:
532 we have no channel from DEVMAP to client ->
533 sending must be initiated by client
534
535 int rc = ipc_data_write_send(phone, device->name, name_size);
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.
545 *
546 */
547static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
548{
549 ipc_callid_t callid;
550 ipc_call_t call;
551 bool cont = true;
552 devmap_driver_t *driver = NULL;
553
554 ipc_answer_0(iid, EOK);
555
556 devmap_driver_register(&driver);
557
558 if (NULL == driver)
559 return;
560
561 while (cont) {
562 callid = async_get_call(&call);
563
564 switch (IPC_GET_METHOD(call)) {
565 case IPC_M_PHONE_HUNGUP:
566 cont = false;
567 continue; /* Exit thread */
568 case DEVMAP_DRIVER_UNREGISTER:
569 if (NULL == driver) {
570 ipc_answer_0(callid, ENOENT);
571 } else {
572 ipc_answer_0(callid, EOK);
573 }
574 break;
575 case DEVMAP_DEVICE_REGISTER:
576 /* Register one instance of device */
577 devmap_device_register(callid, &call, driver);
578 break;
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)) {
591 ipc_answer_0(callid, ENOENT);
592 }
593 }
594 }
595
596 if (NULL != driver) {
597 /*
598 * Unregister the device driver and all its devices.
599 */
600 devmap_driver_unregister(driver);
601 driver = NULL;
602 }
603
604}
605
606/** Handle connection with device client.
607 *
608 */
609static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
610{
611 ipc_callid_t callid;
612 ipc_call_t call;
613 bool cont = true;
614
615 ipc_answer_0(iid, EOK); /* Accept connection */
616
617 while (cont) {
618 callid = async_get_call(&call);
619
620 switch (IPC_GET_METHOD(call)) {
621 case IPC_M_PHONE_HUNGUP:
622 cont = false;
623 continue; /* Exit thread */
624
625 case DEVMAP_DEVICE_GET_HANDLE:
626 devmap_get_handle(callid, &call);
627
628 break;
629 case DEVMAP_DEVICE_GET_NAME:
630 /* TODO */
631 devmap_get_name(callid, &call);
632 break;
633 default:
634 if (!(callid & IPC_CALLID_NOTIFICATION)) {
635 ipc_answer_0(callid, ENOENT);
636 }
637 }
638 }
639}
640
641/** Function for handling connections to devmap
642 *
643 */
644static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
645{
646 /* Select interface */
647 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
648 case DEVMAP_DRIVER:
649 devmap_connection_driver(iid, icall);
650 break;
651 case DEVMAP_CLIENT:
652 devmap_connection_client(iid, icall);
653 break;
654 case DEVMAP_CONNECT_TO_DEVICE:
655 /* Connect client to selected device */
656 devmap_forward(iid, icall);
657 break;
658 default:
659 ipc_answer_0(iid, ENOENT); /* No such interface */
660 }
661
662 /* Cleanup */
663}
664
665/**
666 *
667 */
668int main(int argc, char *argv[])
669{
670 printf(NAME ": HelenOS Device Mapper\n");
671
672 ipcarg_t phonead;
673
674 if (devmap_init() != 0) {
675 printf(NAME ": Error while initializing service\n");
676 return -1;
677 }
678
679 /* Set a handler of incomming connections */
680 async_set_client_connection(devmap_connection);
681
682 /* Register device mapper at naming service */
683 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
684 return -1;
685
686 printf(NAME ": Accepting connections\n");
687 async_manager();
688 /* Never reached */
689 return 0;
690}
691
692/**
693 * @}
694 */
Note: See TracBrowser for help on using the repository browser.