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

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

Merge from lp:~vojtech-horky/helenos/ddf-fixes.

  • Property mode set to 100644
File size: 26.9 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.
[6519d6f]31 * @brief HelenOS device mapper.
[13125d3]32 * @{
[6519d6f]33 */
[13125d3]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>
[1e4cada]44#include <fibril_synch.h>
[798f364]45#include <stdlib.h>
[19f857a]46#include <str.h>
[21c5d41]47#include <ipc/devmap.h>
[13125d3]48
[7fcb74c]49#define NAME "devmap"
50#define NULL_DEVICES 256
[13125d3]51
[cb41a5e]52/** Representation of device driver.
53 *
54 * Each driver is responsible for a set of devices.
55 *
56 */
57typedef struct {
58 /** Pointers to previous and next drivers in linked list */
59 link_t drivers;
60 /** Pointer to the linked list of devices controlled by this driver */
61 link_t devices;
62 /** Phone asociated with this driver */
[96b02eb9]63 sysarg_t phone;
[cb41a5e]64 /** Device driver name */
65 char *name;
[fc02cc41]66 /** Fibril mutex for list of devices owned by this driver */
67 fibril_mutex_t devices_mutex;
[cb41a5e]68} devmap_driver_t;
69
[1313ee9]70/** Info about registered namespaces
71 *
72 */
73typedef struct {
74 /** Pointer to the previous and next device in the list of all namespaces */
75 link_t namespaces;
76 /** Unique namespace identifier */
[991f645]77 devmap_handle_t handle;
[1313ee9]78 /** Namespace name */
79 char *name;
80 /** Reference count */
81 size_t refcnt;
82} devmap_namespace_t;
83
[cb41a5e]84/** Info about registered device
85 *
86 */
87typedef struct {
88 /** Pointer to the previous and next device in the list of all devices */
89 link_t devices;
90 /** Pointer to the previous and next device in the list of devices
91 owned by one driver */
92 link_t driver_devices;
[1313ee9]93 /** Unique device identifier */
[991f645]94 devmap_handle_t handle;
[1313ee9]95 /** Device namespace */
96 devmap_namespace_t *namespace;
[cb41a5e]97 /** Device name */
98 char *name;
99 /** Device driver handling this device */
100 devmap_driver_t *driver;
[47a7174f]101 /** Use this interface when forwarding to driver. */
102 sysarg_t forward_interface;
[cb41a5e]103} devmap_device_t;
104
[798f364]105LIST_INITIALIZE(devices_list);
[1313ee9]106LIST_INITIALIZE(namespaces_list);
[798f364]107LIST_INITIALIZE(drivers_list);
[c07af37]108
[fc02cc41]109/* Locking order:
110 * drivers_list_mutex
111 * devices_list_mutex
112 * (devmap_driver_t *)->devices_mutex
113 * create_handle_mutex
114 **/
115
116static FIBRIL_MUTEX_INITIALIZE(devices_list_mutex);
[1cab2f41]117static FIBRIL_CONDVAR_INITIALIZE(devices_list_cv);
[fc02cc41]118static FIBRIL_MUTEX_INITIALIZE(drivers_list_mutex);
119static FIBRIL_MUTEX_INITIALIZE(create_handle_mutex);
[7fcb74c]120static FIBRIL_MUTEX_INITIALIZE(null_devices_mutex);
[fc02cc41]121
[991f645]122static devmap_handle_t last_handle = 0;
[7fcb74c]123static devmap_device_t *null_devices[NULL_DEVICES];
[cb41a5e]124
[991f645]125static devmap_handle_t devmap_create_handle(void)
[798f364]126{
127 /* TODO: allow reusing old handles after their unregistration
[5d0e461]128 * and implement some version of LRU algorithm, avoid overflow
[6519d6f]129 */
130
[fc02cc41]131 fibril_mutex_lock(&create_handle_mutex);
[cb41a5e]132 last_handle++;
[fc02cc41]133 fibril_mutex_unlock(&create_handle_mutex);
[6519d6f]134
[cb41a5e]135 return last_handle;
[13125d3]136}
137
[1313ee9]138/** Convert fully qualified device name to namespace and device name.
139 *
140 * A fully qualified device name can be either a plain device name
141 * (then the namespace is considered to be an empty string) or consist
142 * of two components separated by a slash. No more than one slash
143 * is allowed.
144 *
145 */
146static bool devmap_fqdn_split(const char *fqdn, char **ns_name, char **name)
147{
148 size_t cnt = 0;
149 size_t slash_offset = 0;
150 size_t slash_after = 0;
151
152 size_t offset = 0;
153 size_t offset_prev = 0;
154 wchar_t c;
155
156 while ((c = str_decode(fqdn, &offset, STR_NO_LIMIT)) != 0) {
157 if (c == '/') {
158 cnt++;
159 slash_offset = offset_prev;
160 slash_after = offset;
161 }
162 offset_prev = offset;
163 }
164
165 /* More than one slash */
166 if (cnt > 1)
167 return false;
168
169 /* No slash -> namespace is empty */
170 if (cnt == 0) {
171 *ns_name = str_dup("");
172 if (*ns_name == NULL)
173 return false;
174
175 *name = str_dup(fqdn);
[ab108be4]176 if (*name == NULL) {
177 free(*ns_name);
178 return false;
179 }
180
181 if (str_cmp(*name, "") == 0) {
182 free(*name);
[1313ee9]183 free(*ns_name);
184 return false;
185 }
186
187 return true;
188 }
189
190 /* Exactly one slash */
191 *ns_name = str_ndup(fqdn, slash_offset);
192 if (*ns_name == NULL)
193 return false;
194
195 *name = str_dup(fqdn + slash_after);
[ab108be4]196 if (*name == NULL) {
197 free(*ns_name);
198 return false;
199 }
200
201 if (str_cmp(*name, "") == 0) {
202 free(*name);
[1313ee9]203 free(*ns_name);
204 return false;
205 }
206
207 return true;
208}
209
210/** Find namespace with given name.
211 *
212 * The devices_list_mutex should be already held when
213 * calling this function.
214 *
215 */
216static devmap_namespace_t *devmap_namespace_find_name(const char *name)
217{
[ab108be4]218 link_t *item;
219 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) {
220 devmap_namespace_t *namespace =
221 list_get_instance(item, devmap_namespace_t, namespaces);
[1313ee9]222 if (str_cmp(namespace->name, name) == 0)
223 return namespace;
224 }
225
226 return NULL;
227}
228
229/** Find namespace with given handle.
230 *
231 * The devices_list_mutex should be already held when
232 * calling this function.
233 *
234 * @todo: use hash table
235 *
236 */
[991f645]237static devmap_namespace_t *devmap_namespace_find_handle(devmap_handle_t handle)
[1313ee9]238{
[ab108be4]239 link_t *item;
240 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) {
241 devmap_namespace_t *namespace =
242 list_get_instance(item, devmap_namespace_t, namespaces);
[1313ee9]243 if (namespace->handle == handle)
244 return namespace;
245 }
246
247 return NULL;
248}
249
[798f364]250/** Find device with given name.
[1313ee9]251 *
252 * The devices_list_mutex should be already held when
253 * calling this function.
[798f364]254 *
255 */
[1313ee9]256static devmap_device_t *devmap_device_find_name(const char *ns_name,
257 const char *name)
[07e4a3c]258{
[ab108be4]259 link_t *item;
260 for (item = devices_list.next; item != &devices_list; item = item->next) {
261 devmap_device_t *device =
262 list_get_instance(item, devmap_device_t, devices);
263 if ((str_cmp(device->namespace->name, ns_name) == 0)
264 && (str_cmp(device->name, name) == 0))
[1313ee9]265 return device;
[798f364]266 }
[6519d6f]267
[1313ee9]268 return NULL;
[798f364]269}
270
271/** Find device with given handle.
[1313ee9]272 *
273 * The devices_list_mutex should be already held when
274 * calling this function.
[6519d6f]275 *
[798f364]276 * @todo: use hash table
[6519d6f]277 *
[798f364]278 */
[991f645]279static devmap_device_t *devmap_device_find_handle(devmap_handle_t handle)
[798f364]280{
[ab108be4]281 link_t *item;
282 for (item = devices_list.next; item != &devices_list; item = item->next) {
283 devmap_device_t *device =
284 list_get_instance(item, devmap_device_t, devices);
[6519d6f]285 if (device->handle == handle)
[1313ee9]286 return device;
[798f364]287 }
[6519d6f]288
[1313ee9]289 return NULL;
290}
291
292/** Create a namespace (if not already present)
293 *
294 * The devices_list_mutex should be already held when
295 * calling this function.
296 *
297 */
298static devmap_namespace_t *devmap_namespace_create(const char *ns_name)
299{
300 devmap_namespace_t *namespace = devmap_namespace_find_name(ns_name);
301 if (namespace != NULL)
302 return namespace;
303
304 namespace = (devmap_namespace_t *) malloc(sizeof(devmap_namespace_t));
305 if (namespace == NULL)
306 return NULL;
307
308 namespace->name = str_dup(ns_name);
309 if (namespace->name == NULL) {
310 free(namespace);
[798f364]311 return NULL;
[fc02cc41]312 }
[6519d6f]313
[1313ee9]314 namespace->handle = devmap_create_handle();
315 namespace->refcnt = 0;
[798f364]316
[1313ee9]317 /*
318 * Insert new namespace into list of registered namespaces
319 */
320 list_append(&(namespace->namespaces), &namespaces_list);
[fc02cc41]321
[1313ee9]322 return namespace;
[798f364]323}
324
[1313ee9]325/** Destroy a namespace (if it is no longer needed)
326 *
327 * The devices_list_mutex should be already held when
328 * calling this function.
329 *
330 */
331static void devmap_namespace_destroy(devmap_namespace_t *namespace)
332{
333 if (namespace->refcnt == 0) {
334 list_remove(&(namespace->namespaces));
335
336 free(namespace->name);
337 free(namespace);
338 }
339}
340
341/** Increase namespace reference count by including device
342 *
343 * The devices_list_mutex should be already held when
344 * calling this function.
345 *
346 */
347static void devmap_namespace_addref(devmap_namespace_t *namespace,
348 devmap_device_t *device)
349{
350 device->namespace = namespace;
351 namespace->refcnt++;
352}
353
354/** Decrease namespace reference count
355 *
356 * The devices_list_mutex should be already held when
357 * calling this function.
358 *
359 */
360static void devmap_namespace_delref(devmap_namespace_t *namespace)
361{
362 namespace->refcnt--;
363 devmap_namespace_destroy(namespace);
364}
365
366/** Unregister device and free it
367 *
368 * The devices_list_mutex should be already held when
369 * calling this function.
370 *
[798f364]371 */
[1313ee9]372static void devmap_device_unregister_core(devmap_device_t *device)
[798f364]373{
[1313ee9]374 devmap_namespace_delref(device->namespace);
[798f364]375 list_remove(&(device->devices));
376 list_remove(&(device->driver_devices));
[6519d6f]377
378 free(device->name);
[798f364]379 free(device);
380}
381
[7f3e3e7]382/**
383 * Read info about new driver and add it into linked list of registered
384 * drivers.
[798f364]385 */
[1313ee9]386static devmap_driver_t *devmap_driver_register(void)
[798f364]387{
[6519d6f]388 ipc_call_t icall;
389 ipc_callid_t iid = async_get_call(&icall);
390
[228e490]391 if (IPC_GET_IMETHOD(icall) != DEVMAP_DRIVER_REGISTER) {
[b74959bd]392 ipc_answer_0(iid, EREFUSED);
[1313ee9]393 return NULL;
[6519d6f]394 }
395
[ab108be4]396 devmap_driver_t *driver =
397 (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
[6519d6f]398 if (driver == NULL) {
[b74959bd]399 ipc_answer_0(iid, ENOMEM);
[1313ee9]400 return NULL;
[07e4a3c]401 }
[6519d6f]402
403 /*
[798f364]404 * Get driver name
405 */
[4cac2d69]406 int rc = async_data_write_accept((void **) &driver->name, true, 0,
[eda925a]407 DEVMAP_NAME_MAXLEN, 0, NULL);
[1313ee9]408 if (rc != EOK) {
[798f364]409 free(driver);
[1313ee9]410 ipc_answer_0(iid, rc);
411 return NULL;
[798f364]412 }
[6519d6f]413
414 /*
[5d0e461]415 * Create connection to the driver
[798f364]416 */
[6519d6f]417 ipc_call_t call;
[1313ee9]418 ipc_callid_t callid = async_get_call(&call);
[6519d6f]419
[228e490]420 if (IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) {
[798f364]421 free(driver->name);
422 free(driver);
[ab108be4]423 ipc_answer_0(callid, ENOTSUP);
[b74959bd]424 ipc_answer_0(iid, ENOTSUP);
[1313ee9]425 return NULL;
[798f364]426 }
[6519d6f]427
[38c706cc]428 driver->phone = IPC_GET_ARG5(call);
[b74959bd]429 ipc_answer_0(callid, EOK);
[798f364]430
[ab108be4]431 /*
432 * Initialize mutex for list of devices
433 * owned by this driver
434 */
435 fibril_mutex_initialize(&driver->devices_mutex);
436
437 /*
438 * Initialize list of asociated devices
439 */
440 list_initialize(&driver->devices);
[798f364]441 list_initialize(&(driver->drivers));
[6519d6f]442
[fc02cc41]443 fibril_mutex_lock(&drivers_list_mutex);
444
[7f3e3e7]445 /* TODO:
[ab108be4]446 * Check that no driver with name equal to
447 * driver->name is registered
[7f3e3e7]448 */
[6519d6f]449
450 /*
[798f364]451 * Insert new driver into list of registered drivers
452 */
453 list_append(&(driver->drivers), &drivers_list);
[fc02cc41]454 fibril_mutex_unlock(&drivers_list_mutex);
[798f364]455
[b74959bd]456 ipc_answer_0(iid, EOK);
[6519d6f]457
[1313ee9]458 return driver;
[07e4a3c]459}
460
[6519d6f]461/**
462 * Unregister device driver, unregister all its devices and free driver
[b74959bd]463 * structure.
[6519d6f]464 *
[798f364]465 */
466static int devmap_driver_unregister(devmap_driver_t *driver)
[07e4a3c]467{
[6519d6f]468 if (driver == NULL)
[798f364]469 return EEXISTS;
[21c5d41]470
[fc02cc41]471 fibril_mutex_lock(&drivers_list_mutex);
472
[cb41a5e]473 if (driver->phone != 0)
474 ipc_hangup(driver->phone);
[798f364]475
[cb41a5e]476 /* Remove it from list of drivers */
[798f364]477 list_remove(&(driver->drivers));
[6519d6f]478
[fc02cc41]479 /* Unregister all its devices */
480 fibril_mutex_lock(&devices_list_mutex);
481 fibril_mutex_lock(&driver->devices_mutex);
482
[798f364]483 while (!list_empty(&(driver->devices))) {
[6519d6f]484 devmap_device_t *device = list_get_instance(driver->devices.next,
[7f3e3e7]485 devmap_device_t, driver_devices);
[798f364]486 devmap_device_unregister_core(device);
487 }
488
[fc02cc41]489 fibril_mutex_unlock(&driver->devices_mutex);
490 fibril_mutex_unlock(&devices_list_mutex);
491 fibril_mutex_unlock(&drivers_list_mutex);
492
[ab108be4]493 /* Free name and driver */
[cb41a5e]494 if (driver->name != NULL)
[798f364]495 free(driver->name);
[6519d6f]496
[798f364]497 free(driver);
[6519d6f]498
[07e4a3c]499 return EOK;
500}
501
[798f364]502/** Register instance of device
503 *
504 */
505static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
[b74959bd]506 devmap_driver_t *driver)
[07e4a3c]507{
[6519d6f]508 if (driver == NULL) {
[b74959bd]509 ipc_answer_0(iid, EREFUSED);
[798f364]510 return;
511 }
512
513 /* Create new device entry */
[ab108be4]514 devmap_device_t *device =
515 (devmap_device_t *) malloc(sizeof(devmap_device_t));
[6519d6f]516 if (device == NULL) {
[b74959bd]517 ipc_answer_0(iid, ENOMEM);
[798f364]518 return;
519 }
520
[47a7174f]521 /* Set the interface, if any. */
522 device->forward_interface = IPC_GET_ARG1(*icall);
523
[1313ee9]524 /* Get fqdn */
525 char *fqdn;
[4cac2d69]526 int rc = async_data_write_accept((void **) &fqdn, true, 0,
[eda925a]527 DEVMAP_NAME_MAXLEN, 0, NULL);
[1313ee9]528 if (rc != EOK) {
[798f364]529 free(device);
[1313ee9]530 ipc_answer_0(iid, rc);
[798f364]531 return;
532 }
[6519d6f]533
[1313ee9]534 char *ns_name;
535 if (!devmap_fqdn_split(fqdn, &ns_name, &device->name)) {
536 free(fqdn);
[798f364]537 free(device);
[1313ee9]538 ipc_answer_0(iid, EINVAL);
[798f364]539 return;
540 }
[21c5d41]541
[1313ee9]542 free(fqdn);
543
544 fibril_mutex_lock(&devices_list_mutex);
[6519d6f]545
[1313ee9]546 devmap_namespace_t *namespace = devmap_namespace_create(ns_name);
547 free(ns_name);
[ab108be4]548 if (namespace == NULL) {
[1313ee9]549 fibril_mutex_unlock(&devices_list_mutex);
[ab108be4]550 free(device->name);
[798f364]551 free(device);
[1313ee9]552 ipc_answer_0(iid, ENOMEM);
[798f364]553 return;
[07e4a3c]554 }
[798f364]555
556 list_initialize(&(device->devices));
557 list_initialize(&(device->driver_devices));
[6519d6f]558
[1313ee9]559 /* Check that device is not already registered */
560 if (devmap_device_find_name(namespace->name, device->name) != NULL) {
[ab108be4]561 printf("%s: Device '%s/%s' already registered\n", NAME,
[7e752b2]562 device->namespace->name, device->name);
[1313ee9]563 devmap_namespace_destroy(namespace);
[fc02cc41]564 fibril_mutex_unlock(&devices_list_mutex);
[798f364]565 free(device->name);
566 free(device);
[b74959bd]567 ipc_answer_0(iid, EEXISTS);
[798f364]568 return;
[07e4a3c]569 }
[6519d6f]570
[798f364]571 /* Get unique device handle */
[6519d6f]572 device->handle = devmap_create_handle();
[47a7174f]573
[1313ee9]574 devmap_namespace_addref(namespace, device);
[798f364]575 device->driver = driver;
[07e4a3c]576
[798f364]577 /* Insert device into list of all devices */
[b74959bd]578 list_append(&device->devices, &devices_list);
[6519d6f]579
[fc02cc41]580 /* Insert device into list of devices that belog to one driver */
581 fibril_mutex_lock(&device->driver->devices_mutex);
582
[b74959bd]583 list_append(&device->driver_devices, &device->driver->devices);
[798f364]584
[fc02cc41]585 fibril_mutex_unlock(&device->driver->devices_mutex);
[1cab2f41]586 fibril_condvar_broadcast(&devices_list_cv);
[fc02cc41]587 fibril_mutex_unlock(&devices_list_mutex);
588
[b74959bd]589 ipc_answer_1(iid, EOK, device->handle);
[07e4a3c]590}
591
[798f364]592/**
593 *
594 */
595static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
[b74959bd]596 devmap_driver_t *driver)
[07e4a3c]597{
[798f364]598 /* TODO */
[07e4a3c]599 return EOK;
600}
[13125d3]601
[798f364]602/** Connect client to the device.
[6519d6f]603 *
[798f364]604 * Find device driver owning requested device and forward
605 * the message to it.
[6519d6f]606 *
[798f364]607 */
608static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
609{
[1313ee9]610 fibril_mutex_lock(&devices_list_mutex);
611
[798f364]612 /*
613 * Get handle from request
614 */
[991f645]615 devmap_handle_t handle = IPC_GET_ARG2(*call);
[6519d6f]616 devmap_device_t *dev = devmap_device_find_handle(handle);
617
[cb41a5e]618 if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) {
[ab108be4]619 fibril_mutex_unlock(&devices_list_mutex);
[b74959bd]620 ipc_answer_0(callid, ENOENT);
[798f364]621 return;
[6519d6f]622 }
623
[47a7174f]624 if (dev->forward_interface == 0) {
625 ipc_forward_fast(callid, dev->driver->phone,
626 dev->handle, 0, 0,
627 IPC_FF_NONE);
628 } else {
629 ipc_forward_fast(callid, dev->driver->phone,
630 dev->forward_interface, dev->handle, 0,
631 IPC_FF_NONE);
632 }
[1313ee9]633
634 fibril_mutex_unlock(&devices_list_mutex);
[798f364]635}
636
637/** Find handle for device instance identified by name.
[6519d6f]638 *
[798f364]639 * In answer will be send EOK and device handle in arg1 or a error
[6519d6f]640 * code from errno.h.
641 *
[798f364]642 */
[1313ee9]643static void devmap_device_get_handle(ipc_callid_t iid, ipc_call_t *icall)
[798f364]644{
[1313ee9]645 char *fqdn;
[6519d6f]646
[1313ee9]647 /* Get fqdn */
[4cac2d69]648 int rc = async_data_write_accept((void **) &fqdn, true, 0,
[eda925a]649 DEVMAP_NAME_MAXLEN, 0, NULL);
[1313ee9]650 if (rc != EOK) {
651 ipc_answer_0(iid, rc);
[798f364]652 return;
653 }
[6519d6f]654
[1313ee9]655 char *ns_name;
656 char *name;
657 if (!devmap_fqdn_split(fqdn, &ns_name, &name)) {
658 free(fqdn);
659 ipc_answer_0(iid, EINVAL);
[798f364]660 return;
[6519d6f]661 }
662
[1313ee9]663 free(fqdn);
[6519d6f]664
[c07af37]665 fibril_mutex_lock(&devices_list_mutex);
[1cab2f41]666 const devmap_device_t *dev;
[1313ee9]667
[1cab2f41]668recheck:
[1313ee9]669
[798f364]670 /*
[1cab2f41]671 * Find device name in the list of known devices.
[798f364]672 */
[1313ee9]673 dev = devmap_device_find_name(ns_name, name);
[6519d6f]674
[798f364]675 /*
676 * Device was not found.
677 */
[6519d6f]678 if (dev == NULL) {
679 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
[1cab2f41]680 /* Blocking lookup */
681 fibril_condvar_wait(&devices_list_cv,
682 &devices_list_mutex);
683 goto recheck;
[6519d6f]684 }
685
[b74959bd]686 ipc_answer_0(iid, ENOENT);
[1313ee9]687 free(ns_name);
[6519d6f]688 free(name);
[c07af37]689 fibril_mutex_unlock(&devices_list_mutex);
[798f364]690 return;
691 }
[6519d6f]692
[b74959bd]693 ipc_answer_1(iid, EOK, dev->handle);
[ab108be4]694
695 fibril_mutex_unlock(&devices_list_mutex);
[1313ee9]696 free(ns_name);
[6519d6f]697 free(name);
[798f364]698}
699
[1313ee9]700/** Find handle for namespace identified by name.
701 *
702 * In answer will be send EOK and device handle in arg1 or a error
703 * code from errno.h.
[798f364]704 *
705 */
[1313ee9]706static void devmap_namespace_get_handle(ipc_callid_t iid, ipc_call_t *icall)
[798f364]707{
[1313ee9]708 char *name;
709
710 /* Get device name */
[4cac2d69]711 int rc = async_data_write_accept((void **) &name, true, 0,
[eda925a]712 DEVMAP_NAME_MAXLEN, 0, NULL);
[1313ee9]713 if (rc != EOK) {
714 ipc_answer_0(iid, rc);
715 return;
716 }
717
718 fibril_mutex_lock(&devices_list_mutex);
719 const devmap_namespace_t *namespace;
720
721recheck:
[6519d6f]722
[798f364]723 /*
[1313ee9]724 * Find namespace name in the list of known namespaces.
[798f364]725 */
[1313ee9]726 namespace = devmap_namespace_find_name(name);
727
728 /*
729 * Namespace was not found.
730 */
731 if (namespace == NULL) {
732 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
733 /* Blocking lookup */
734 fibril_condvar_wait(&devices_list_cv,
735 &devices_list_mutex);
736 goto recheck;
737 }
738
[b74959bd]739 ipc_answer_0(iid, ENOENT);
[1313ee9]740 free(name);
741 fibril_mutex_unlock(&devices_list_mutex);
[798f364]742 return;
743 }
[6519d6f]744
[1313ee9]745 ipc_answer_1(iid, EOK, namespace->handle);
[ab108be4]746
747 fibril_mutex_unlock(&devices_list_mutex);
[1313ee9]748 free(name);
749}
750
751static void devmap_handle_probe(ipc_callid_t iid, ipc_call_t *icall)
752{
753 fibril_mutex_lock(&devices_list_mutex);
[6519d6f]754
[ab108be4]755 devmap_namespace_t *namespace =
756 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
[1313ee9]757 if (namespace == NULL) {
[ab108be4]758 devmap_device_t *dev =
759 devmap_device_find_handle(IPC_GET_ARG1(*icall));
[1313ee9]760 if (dev == NULL)
761 ipc_answer_1(iid, EOK, DEV_HANDLE_NONE);
762 else
763 ipc_answer_1(iid, EOK, DEV_HANDLE_DEVICE);
764 } else
765 ipc_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE);
[6519d6f]766
[1313ee9]767 fibril_mutex_unlock(&devices_list_mutex);
[798f364]768}
769
[1313ee9]770static void devmap_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
[cb41a5e]771{
[fc02cc41]772 fibril_mutex_lock(&devices_list_mutex);
[1313ee9]773 ipc_answer_1(iid, EOK, list_count(&namespaces_list));
[fc02cc41]774 fibril_mutex_unlock(&devices_list_mutex);
[cb41a5e]775}
776
[1313ee9]777static void devmap_get_device_count(ipc_callid_t iid, ipc_call_t *icall)
[cb41a5e]778{
[fc02cc41]779 fibril_mutex_lock(&devices_list_mutex);
780
[ab108be4]781 devmap_namespace_t *namespace =
782 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
[1313ee9]783 if (namespace == NULL)
784 ipc_answer_0(iid, EEXISTS);
785 else
786 ipc_answer_1(iid, EOK, namespace->refcnt);
787
788 fibril_mutex_unlock(&devices_list_mutex);
789}
790
791static void devmap_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
792{
[cb41a5e]793 ipc_callid_t callid;
794 size_t size;
[0da4e41]795 if (!async_data_read_receive(&callid, &size)) {
[cb41a5e]796 ipc_answer_0(callid, EREFUSED);
797 ipc_answer_0(iid, EREFUSED);
798 return;
799 }
800
801 if ((size % sizeof(dev_desc_t)) != 0) {
802 ipc_answer_0(callid, EINVAL);
[1313ee9]803 ipc_answer_0(iid, EINVAL);
[cb41a5e]804 return;
805 }
806
[1313ee9]807 fibril_mutex_lock(&devices_list_mutex);
808
[5d0e461]809 size_t count = size / sizeof(dev_desc_t);
[1313ee9]810 if (count != list_count(&namespaces_list)) {
[ab108be4]811 fibril_mutex_unlock(&devices_list_mutex);
[1313ee9]812 ipc_answer_0(callid, EOVERFLOW);
813 ipc_answer_0(iid, EOVERFLOW);
814 return;
815 }
816
[cb41a5e]817 dev_desc_t *desc = (dev_desc_t *) malloc(size);
818 if (desc == NULL) {
[ab108be4]819 fibril_mutex_unlock(&devices_list_mutex);
[cb41a5e]820 ipc_answer_0(callid, ENOMEM);
[1313ee9]821 ipc_answer_0(iid, ENOMEM);
[cb41a5e]822 return;
823 }
824
[ab108be4]825 link_t *item;
[5d0e461]826 size_t pos = 0;
[ab108be4]827 for (item = namespaces_list.next; item != &namespaces_list;
828 item = item->next) {
829 devmap_namespace_t *namespace =
830 list_get_instance(item, devmap_namespace_t, namespaces);
[cb41a5e]831
[1313ee9]832 desc[pos].handle = namespace->handle;
833 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, namespace->name);
[cb41a5e]834 pos++;
835 }
836
[96b02eb9]837 sysarg_t retval = async_data_read_finalize(callid, desc, size);
[1313ee9]838
839 free(desc);
840 fibril_mutex_unlock(&devices_list_mutex);
841
842 ipc_answer_0(iid, retval);
843}
844
845static void devmap_get_devices(ipc_callid_t iid, ipc_call_t *icall)
846{
847 /* FIXME: Use faster algorithm which can make better use
848 of namespaces */
849
850 ipc_callid_t callid;
851 size_t size;
852 if (!async_data_read_receive(&callid, &size)) {
853 ipc_answer_0(callid, EREFUSED);
[cb41a5e]854 ipc_answer_0(iid, EREFUSED);
855 return;
856 }
857
[1313ee9]858 if ((size % sizeof(dev_desc_t)) != 0) {
859 ipc_answer_0(callid, EINVAL);
860 ipc_answer_0(iid, EINVAL);
861 return;
862 }
863
864 fibril_mutex_lock(&devices_list_mutex);
865
[ab108be4]866 devmap_namespace_t *namespace =
867 devmap_namespace_find_handle(IPC_GET_ARG1(*icall));
[1313ee9]868 if (namespace == NULL) {
869 fibril_mutex_unlock(&devices_list_mutex);
870 ipc_answer_0(callid, ENOENT);
871 ipc_answer_0(iid, ENOENT);
872 return;
873 }
874
875 size_t count = size / sizeof(dev_desc_t);
876 if (count != namespace->refcnt) {
[ab108be4]877 fibril_mutex_unlock(&devices_list_mutex);
[1313ee9]878 ipc_answer_0(callid, EOVERFLOW);
879 ipc_answer_0(iid, EOVERFLOW);
880 return;
881 }
882
883 dev_desc_t *desc = (dev_desc_t *) malloc(size);
884 if (desc == NULL) {
[ab108be4]885 fibril_mutex_unlock(&devices_list_mutex);
[1313ee9]886 ipc_answer_0(callid, ENOMEM);
887 ipc_answer_0(iid, EREFUSED);
888 return;
889 }
[cb41a5e]890
[ab108be4]891 link_t *item;
[1313ee9]892 size_t pos = 0;
[ab108be4]893 for (item = devices_list.next; item != &devices_list; item = item->next) {
894 devmap_device_t *device =
895 list_get_instance(item, devmap_device_t, devices);
[1313ee9]896
897 if (device->namespace == namespace) {
898 desc[pos].handle = device->handle;
899 str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, device->name);
900 pos++;
901 }
902 }
903
[96b02eb9]904 sysarg_t retval = async_data_read_finalize(callid, desc, size);
[1313ee9]905
906 free(desc);
[fc02cc41]907 fibril_mutex_unlock(&devices_list_mutex);
908
[1313ee9]909 ipc_answer_0(iid, retval);
[cb41a5e]910}
911
[7fcb74c]912static void devmap_null_create(ipc_callid_t iid, ipc_call_t *icall)
[cb41a5e]913{
[7fcb74c]914 fibril_mutex_lock(&null_devices_mutex);
915
916 unsigned int i;
917 bool fnd = false;
918
919 for (i = 0; i < NULL_DEVICES; i++) {
920 if (null_devices[i] == NULL) {
921 fnd = true;
922 break;
923 }
924 }
925
926 if (!fnd) {
927 fibril_mutex_unlock(&null_devices_mutex);
928 ipc_answer_0(iid, ENOMEM);
929 return;
930 }
931
[1313ee9]932 char null[DEVMAP_NAME_MAXLEN];
933 snprintf(null, DEVMAP_NAME_MAXLEN, "%u", i);
934
935 char *dev_name = str_dup(null);
936 if (dev_name == NULL) {
937 fibril_mutex_unlock(&null_devices_mutex);
938 ipc_answer_0(iid, ENOMEM);
939 return;
940 }
941
[ab108be4]942 devmap_device_t *device =
943 (devmap_device_t *) malloc(sizeof(devmap_device_t));
[7fcb74c]944 if (device == NULL) {
945 fibril_mutex_unlock(&null_devices_mutex);
946 ipc_answer_0(iid, ENOMEM);
947 return;
948 }
[cb41a5e]949
[1313ee9]950 fibril_mutex_lock(&devices_list_mutex);
[7fcb74c]951
[1313ee9]952 devmap_namespace_t *namespace = devmap_namespace_create("null");
[ab108be4]953 if (namespace == NULL) {
[1313ee9]954 fibril_mutex_lock(&devices_list_mutex);
[7fcb74c]955 fibril_mutex_unlock(&null_devices_mutex);
956 ipc_answer_0(iid, ENOMEM);
957 return;
[cb41a5e]958 }
959
960 list_initialize(&(device->devices));
961 list_initialize(&(device->driver_devices));
962
963 /* Get unique device handle */
964 device->handle = devmap_create_handle();
965 device->driver = NULL;
966
[1313ee9]967 devmap_namespace_addref(namespace, device);
968 device->name = dev_name;
969
[7fcb74c]970 /* Insert device into list of all devices
971 and into null devices array */
[cb41a5e]972 list_append(&device->devices, &devices_list);
[7fcb74c]973 null_devices[i] = device;
[cb41a5e]974
[fc02cc41]975 fibril_mutex_unlock(&devices_list_mutex);
[7fcb74c]976 fibril_mutex_unlock(&null_devices_mutex);
977
[96b02eb9]978 ipc_answer_1(iid, EOK, (sysarg_t) i);
[7fcb74c]979}
980
981static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
982{
[96b02eb9]983 sysarg_t i = IPC_GET_ARG1(*icall);
[ab108be4]984 if (i >= NULL_DEVICES) {
985 ipc_answer_0(iid, ELIMIT);
986 return;
987 }
988
989 fibril_mutex_lock(&null_devices_mutex);
[7fcb74c]990
991 if (null_devices[i] == NULL) {
[ab108be4]992 fibril_mutex_unlock(&null_devices_mutex);
[7fcb74c]993 ipc_answer_0(iid, ENOENT);
994 return;
995 }
996
[1313ee9]997 fibril_mutex_lock(&devices_list_mutex);
[7fcb74c]998 devmap_device_unregister_core(null_devices[i]);
[1313ee9]999 fibril_mutex_unlock(&devices_list_mutex);
1000
[7fcb74c]1001 null_devices[i] = NULL;
1002
1003 fibril_mutex_unlock(&null_devices_mutex);
1004 ipc_answer_0(iid, EOK);
1005}
1006
1007/** Initialize device mapper.
1008 *
1009 *
1010 */
1011static bool devmap_init(void)
1012{
1013 fibril_mutex_lock(&null_devices_mutex);
1014
1015 unsigned int i;
1016 for (i = 0; i < NULL_DEVICES; i++)
1017 null_devices[i] = NULL;
1018
1019 fibril_mutex_unlock(&null_devices_mutex);
[fc02cc41]1020
[cb41a5e]1021 return true;
1022}
1023
[798f364]1024/** Handle connection with device driver.
[13125d3]1025 *
1026 */
[21c5d41]1027static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
[13125d3]1028{
[6519d6f]1029 /* Accept connection */
1030 ipc_answer_0(iid, EOK);
1031
[1313ee9]1032 devmap_driver_t *driver = devmap_driver_register();
1033 if (driver == NULL)
[798f364]1034 return;
1035
[6519d6f]1036 bool cont = true;
[07e4a3c]1037 while (cont) {
[6519d6f]1038 ipc_call_t call;
1039 ipc_callid_t callid = async_get_call(&call);
1040
[228e490]1041 switch (IPC_GET_IMETHOD(call)) {
[13125d3]1042 case IPC_M_PHONE_HUNGUP:
[07e4a3c]1043 cont = false;
[6519d6f]1044 continue;
[798f364]1045 case DEVMAP_DRIVER_UNREGISTER:
[6519d6f]1046 if (NULL == driver)
[b74959bd]1047 ipc_answer_0(callid, ENOENT);
[6519d6f]1048 else
[b74959bd]1049 ipc_answer_0(callid, EOK);
[07e4a3c]1050 break;
[798f364]1051 case DEVMAP_DEVICE_REGISTER:
1052 /* Register one instance of device */
1053 devmap_device_register(callid, &call, driver);
[07e4a3c]1054 break;
[798f364]1055 case DEVMAP_DEVICE_UNREGISTER:
1056 /* Remove instance of device identified by handler */
1057 devmap_device_unregister(callid, &call, driver);
1058 break;
1059 case DEVMAP_DEVICE_GET_HANDLE:
[1313ee9]1060 devmap_device_get_handle(callid, &call);
[798f364]1061 break;
[1313ee9]1062 case DEVMAP_NAMESPACE_GET_HANDLE:
1063 devmap_namespace_get_handle(callid, &call);
[798f364]1064 break;
1065 default:
[cb819f9]1066 ipc_answer_0(callid, ENOENT);
[798f364]1067 }
1068 }
1069
[7fcb74c]1070 if (driver != NULL) {
[6519d6f]1071 /*
[b74959bd]1072 * Unregister the device driver and all its devices.
1073 */
[798f364]1074 devmap_driver_unregister(driver);
1075 driver = NULL;
1076 }
1077}
1078
1079/** Handle connection with device client.
1080 *
1081 */
[21c5d41]1082static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
[798f364]1083{
[6519d6f]1084 /* Accept connection */
1085 ipc_answer_0(iid, EOK);
1086
[798f364]1087 bool cont = true;
1088 while (cont) {
[6519d6f]1089 ipc_call_t call;
1090 ipc_callid_t callid = async_get_call(&call);
1091
[228e490]1092 switch (IPC_GET_IMETHOD(call)) {
[798f364]1093 case IPC_M_PHONE_HUNGUP:
[07e4a3c]1094 cont = false;
[6519d6f]1095 continue;
[798f364]1096 case DEVMAP_DEVICE_GET_HANDLE:
[1313ee9]1097 devmap_device_get_handle(callid, &call);
1098 break;
1099 case DEVMAP_NAMESPACE_GET_HANDLE:
1100 devmap_namespace_get_handle(callid, &call);
[07e4a3c]1101 break;
[1313ee9]1102 case DEVMAP_HANDLE_PROBE:
1103 devmap_handle_probe(callid, &call);
[13125d3]1104 break;
[1313ee9]1105 case DEVMAP_NULL_CREATE:
[7fcb74c]1106 devmap_null_create(callid, &call);
1107 break;
[1313ee9]1108 case DEVMAP_NULL_DESTROY:
[7fcb74c]1109 devmap_null_destroy(callid, &call);
1110 break;
[1313ee9]1111 case DEVMAP_GET_NAMESPACE_COUNT:
1112 devmap_get_namespace_count(callid, &call);
1113 break;
1114 case DEVMAP_GET_DEVICE_COUNT:
1115 devmap_get_device_count(callid, &call);
1116 break;
1117 case DEVMAP_GET_NAMESPACES:
1118 devmap_get_namespaces(callid, &call);
[cb41a5e]1119 break;
[1313ee9]1120 case DEVMAP_GET_DEVICES:
[cb41a5e]1121 devmap_get_devices(callid, &call);
1122 break;
[13125d3]1123 default:
[cb819f9]1124 ipc_answer_0(callid, ENOENT);
[13125d3]1125 }
1126 }
1127}
1128
[6519d6f]1129/** Function for handling connections to devmap
[798f364]1130 *
1131 */
[21c5d41]1132static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
[798f364]1133{
[21c5d41]1134 /* Select interface */
[96b02eb9]1135 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
[7f3e3e7]1136 case DEVMAP_DRIVER:
1137 devmap_connection_driver(iid, icall);
1138 break;
1139 case DEVMAP_CLIENT:
1140 devmap_connection_client(iid, icall);
1141 break;
[b61d47d]1142 case DEVMAP_CONNECT_TO_DEVICE:
[21c5d41]1143 /* Connect client to selected device */
[b61d47d]1144 devmap_forward(iid, icall);
1145 break;
[7f3e3e7]1146 default:
[6519d6f]1147 /* No such interface */
[5d0e461]1148 ipc_answer_0(iid, ENOENT);
[798f364]1149 }
1150}
[13125d3]1151
[798f364]1152/**
1153 *
1154 */
[13125d3]1155int main(int argc, char *argv[])
1156{
[ab108be4]1157 printf("%s: HelenOS Device Mapper\n", NAME);
[21c5d41]1158
[cb41a5e]1159 if (!devmap_init()) {
[ab108be4]1160 printf("%s: Error while initializing service\n", NAME);
[13125d3]1161 return -1;
1162 }
[21c5d41]1163
[c07af37]1164 /* Set a handler of incomming connections */
[798f364]1165 async_set_client_connection(devmap_connection);
[7fcb74c]1166
[7f3e3e7]1167 /* Register device mapper at naming service */
[96b02eb9]1168 sysarg_t phonead;
[38c706cc]1169 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
[13125d3]1170 return -1;
1171
[ab108be4]1172 printf("%s: Accepting connections\n", NAME);
[13125d3]1173 async_manager();
[6519d6f]1174
[13125d3]1175 /* Never reached */
1176 return 0;
1177}
1178
[fb623e2]1179/**
[13125d3]1180 * @}
1181 */
Note: See TracBrowser for help on using the repository browser.