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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9239333 was 92fd52d7, checked in by Jiri Svoboda <jirik.svoboda@…>, 17 years ago

Nuke strcpy() and strcmp().

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