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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b4cbef1 was 472c09d, checked in by Martin Decky <martin@…>, 15 years ago

more consistent naming scheme:

async_data_blob_receive → async_data_receive
async_data_string_receive → async_string_receive
CLIPBOARD_TAG_BLOB → CLIPBOARD_TAG_DATA

async_data_receive can now check the granularity of the received data
async_string_receive can now return the raw size of the received data

replace several common patterns of async_data_write_receive/_finalize
with a single async_data_receive/_string_receive (this greatly improves
code readability)

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