Subversion Repositories HelenOS

Rev

Rev 4348 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2547 cejka 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.
4345 svoboda 31
 * @brief HelenOS device mapper.
2547 cejka 32
 * @{
4345 svoboda 33
 */
2547 cejka 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>
2555 cejka 43
#include <bool.h>
4691 svoboda 44
#include <fibril_sync.h>
2594 cejka 45
#include <stdlib.h>
46
#include <string.h>
3150 svoboda 47
#include <ipc/devmap.h>
2547 cejka 48
 
4691 svoboda 49
#define NAME          "devmap"
50
#define NULL_DEVICES  256
2547 cejka 51
 
4691 svoboda 52
/** Representation of device driver.
53
 *
54
 * Each driver is responsible for a set of devices.
55
 *
56
 */
4345 svoboda 57
typedef struct {
4691 svoboda 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;
66
    /** Fibril mutex for list of devices owned by this driver */
67
    fibril_mutex_t devices_mutex;
68
} devmap_driver_t;
2555 cejka 69
 
4691 svoboda 70
/** Info about registered device
71
 *
72
 */
73
typedef struct {
74
    /** Pointer to the previous and next device in the list of all devices */
75
    link_t devices;
76
    /** Pointer to the previous and next device in the list of devices
77
        owned by one driver */
78
    link_t driver_devices;
79
    /** Unique device identifier  */
80
    dev_handle_t handle;
81
    /** Device name */
82
    char *name;
83
    /** Device driver handling this device */
84
    devmap_driver_t *driver;
85
} devmap_device_t;
86
 
2594 cejka 87
LIST_INITIALIZE(devices_list);
88
LIST_INITIALIZE(drivers_list);
2555 cejka 89
 
4345 svoboda 90
/* Locking order:
4691 svoboda 91
 *  drivers_list_mutex
92
 *  devices_list_mutex
93
 *  (devmap_driver_t *)->devices_mutex
94
 *  create_handle_mutex
2594 cejka 95
 **/
96
 
4691 svoboda 97
static FIBRIL_MUTEX_INITIALIZE(devices_list_mutex);
98
static FIBRIL_CONDVAR_INITIALIZE(devices_list_cv);
99
static FIBRIL_MUTEX_INITIALIZE(drivers_list_mutex);
100
static FIBRIL_MUTEX_INITIALIZE(create_handle_mutex);
101
static FIBRIL_MUTEX_INITIALIZE(null_devices_mutex);
2594 cejka 102
 
4691 svoboda 103
static dev_handle_t last_handle = 0;
104
static devmap_device_t *null_devices[NULL_DEVICES];
105
 
106
static dev_handle_t devmap_create_handle(void)
2594 cejka 107
{
108
    /* TODO: allow reusing old handles after their unregistration
4691 svoboda 109
     * and implement some version of LRU algorithm, avoid overflow
4345 svoboda 110
     */
111
 
4691 svoboda 112
    fibril_mutex_lock(&create_handle_mutex);
113
    last_handle++;
114
    fibril_mutex_unlock(&create_handle_mutex);
4345 svoboda 115
 
4691 svoboda 116
    return last_handle;
2594 cejka 117
}
118
 
119
/** Find device with given name.
120
 *
121
 */
122
static devmap_device_t *devmap_device_find_name(const char *name)
2555 cejka 123
{
4345 svoboda 124
    link_t *item = devices_list.next;
2594 cejka 125
    devmap_device_t *device = NULL;
4345 svoboda 126
 
2594 cejka 127
    while (item != &devices_list) {
128
        device = list_get_instance(item, devmap_device_t, devices);
4691 svoboda 129
        if (str_cmp(device->name, name) == 0)
2594 cejka 130
            break;
131
        item = item->next;
132
    }
4345 svoboda 133
 
3150 svoboda 134
    if (item == &devices_list)
2594 cejka 135
        return NULL;
4345 svoboda 136
 
2594 cejka 137
    device = list_get_instance(item, devmap_device_t, devices);
138
    return device;
139
}
140
 
141
/** Find device with given handle.
4345 svoboda 142
 *
2594 cejka 143
 * @todo: use hash table
4345 svoboda 144
 *
2594 cejka 145
 */
4691 svoboda 146
static devmap_device_t *devmap_device_find_handle(dev_handle_t handle)
2594 cejka 147
{
4691 svoboda 148
    fibril_mutex_lock(&devices_list_mutex);
4345 svoboda 149
 
150
    link_t *item = (&devices_list)->next;
2594 cejka 151
    devmap_device_t *device = NULL;
152
 
153
    while (item != &devices_list) {
154
        device = list_get_instance(item, devmap_device_t, devices);
4345 svoboda 155
        if (device->handle == handle)
2594 cejka 156
            break;
157
        item = item->next;
158
    }
4345 svoboda 159
 
2594 cejka 160
    if (item == &devices_list) {
4691 svoboda 161
        fibril_mutex_unlock(&devices_list_mutex);
2594 cejka 162
        return NULL;
163
    }
4345 svoboda 164
 
2594 cejka 165
    device = list_get_instance(item, devmap_device_t, devices);
166
 
4691 svoboda 167
    fibril_mutex_unlock(&devices_list_mutex);
4345 svoboda 168
 
2594 cejka 169
    return device;
170
}
171
 
2598 jermar 172
/**
173
 * Unregister device and free it. It's assumed that driver's device list is
174
 * already locked.
2594 cejka 175
 */
176
static int devmap_device_unregister_core(devmap_device_t *device)
177
{
178
    list_remove(&(device->devices));
179
    list_remove(&(device->driver_devices));
4345 svoboda 180
 
181
    free(device->name);
2594 cejka 182
    free(device);
4345 svoboda 183
 
2594 cejka 184
    return EOK;
185
}
186
 
2598 jermar 187
/**
188
 * Read info about new driver and add it into linked list of registered
189
 * drivers.
2594 cejka 190
 */
191
static void devmap_driver_register(devmap_driver_t **odriver)
192
{
193
    *odriver = NULL;
2555 cejka 194
 
4345 svoboda 195
    ipc_call_t icall;
196
    ipc_callid_t iid = async_get_call(&icall);
197
 
2594 cejka 198
    if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
2619 jermar 199
        ipc_answer_0(iid, EREFUSED);
2594 cejka 200
        return;
4345 svoboda 201
    }
202
 
203
    devmap_driver_t *driver = (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
204
 
205
    if (driver == NULL) {
2619 jermar 206
        ipc_answer_0(iid, ENOMEM);
2594 cejka 207
        return;
2555 cejka 208
    }
4345 svoboda 209
 
210
    /*
2594 cejka 211
     * Get driver name
212
     */
4345 svoboda 213
    ipc_callid_t callid;
214
    size_t name_size;
2676 jermar 215
    if (!ipc_data_write_receive(&callid, &name_size)) {
2594 cejka 216
        free(driver);
2619 jermar 217
        ipc_answer_0(callid, EREFUSED);
218
        ipc_answer_0(iid, EREFUSED);
2594 cejka 219
        return;
2555 cejka 220
    }
4345 svoboda 221
 
2594 cejka 222
    if (name_size > DEVMAP_NAME_MAXLEN) {
223
        free(driver);
2619 jermar 224
        ipc_answer_0(callid, EINVAL);
225
        ipc_answer_0(iid, EREFUSED);
2594 cejka 226
        return;
227
    }
4345 svoboda 228
 
2594 cejka 229
    /*
230
     * Allocate buffer for device name.
231
     */
4345 svoboda 232
    driver->name = (char *) malloc(name_size + 1);
233
    if (driver->name == NULL) {
2594 cejka 234
        free(driver);
2619 jermar 235
        ipc_answer_0(callid, ENOMEM);
236
        ipc_answer_0(iid, EREFUSED);
2594 cejka 237
        return;
4345 svoboda 238
    }
239
 
2594 cejka 240
    /*
241
     * Send confirmation to sender and get data into buffer.
242
     */
4691 svoboda 243
    if (ipc_data_write_finalize(callid, driver->name, name_size) != EOK) {
2594 cejka 244
        free(driver->name);
245
        free(driver);
2619 jermar 246
        ipc_answer_0(iid, EREFUSED);
2594 cejka 247
        return;
248
    }
4345 svoboda 249
 
2594 cejka 250
    driver->name[name_size] = 0;
4345 svoboda 251
 
4691 svoboda 252
    /* Initialize mutex for list of devices owned by this driver */
253
    fibril_mutex_initialize(&driver->devices_mutex);
4345 svoboda 254
 
255
    /*
2594 cejka 256
     * Initialize list of asociated devices
2619 jermar 257
     */
4691 svoboda 258
    list_initialize(&driver->devices);
4345 svoboda 259
 
260
    /*
4691 svoboda 261
     * Create connection to the driver
2594 cejka 262
     */
4345 svoboda 263
    ipc_call_t call;
2594 cejka 264
    callid = async_get_call(&call);
4345 svoboda 265
 
4691 svoboda 266
    if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
2619 jermar 267
        ipc_answer_0(callid, ENOTSUP);
2594 cejka 268
 
269
        free(driver->name);
270
        free(driver);
2619 jermar 271
        ipc_answer_0(iid, ENOTSUP);
2594 cejka 272
        return;
273
    }
4345 svoboda 274
 
2637 cejka 275
    driver->phone = IPC_GET_ARG5(call);
2594 cejka 276
 
2619 jermar 277
    ipc_answer_0(callid, EOK);
2594 cejka 278
 
279
    list_initialize(&(driver->drivers));
280
 
4691 svoboda 281
    fibril_mutex_lock(&drivers_list_mutex);
4345 svoboda 282
 
2598 jermar 283
    /* TODO:
284
     * check that no driver with name equal to driver->name is registered
285
     */
4345 svoboda 286
 
287
    /*
2594 cejka 288
     * Insert new driver into list of registered drivers
289
     */
290
    list_append(&(driver->drivers), &drivers_list);
4691 svoboda 291
    fibril_mutex_unlock(&drivers_list_mutex);
2594 cejka 292
 
2619 jermar 293
    ipc_answer_0(iid, EOK);
4345 svoboda 294
 
2594 cejka 295
    *odriver = driver;
2555 cejka 296
}
297
 
4345 svoboda 298
/**
299
 * Unregister device driver, unregister all its devices and free driver
2619 jermar 300
 * structure.
4345 svoboda 301
 *
2594 cejka 302
 */
303
static int devmap_driver_unregister(devmap_driver_t *driver)
2555 cejka 304
{
4345 svoboda 305
    if (driver == NULL)
2594 cejka 306
        return EEXISTS;
3150 svoboda 307
 
4691 svoboda 308
    fibril_mutex_lock(&drivers_list_mutex);
4345 svoboda 309
 
4691 svoboda 310
    if (driver->phone != 0)
311
        ipc_hangup(driver->phone);
2594 cejka 312
 
4691 svoboda 313
    /* Remove it from list of drivers */
2594 cejka 314
    list_remove(&(driver->drivers));
4345 svoboda 315
 
4691 svoboda 316
    /* Unregister all its devices */
317
    fibril_mutex_lock(&devices_list_mutex);
318
    fibril_mutex_lock(&driver->devices_mutex);
2594 cejka 319
 
320
    while (!list_empty(&(driver->devices))) {
4345 svoboda 321
        devmap_device_t *device = list_get_instance(driver->devices.next,
2598 jermar 322
            devmap_device_t, driver_devices);
2594 cejka 323
        devmap_device_unregister_core(device);
324
    }
325
 
4691 svoboda 326
    fibril_mutex_unlock(&driver->devices_mutex);
327
    fibril_mutex_unlock(&devices_list_mutex);
328
    fibril_mutex_unlock(&drivers_list_mutex);
4345 svoboda 329
 
2594 cejka 330
    /* free name and driver */
4691 svoboda 331
    if (driver->name != NULL)
2594 cejka 332
        free(driver->name);
4345 svoboda 333
 
2594 cejka 334
    free(driver);
4345 svoboda 335
 
2555 cejka 336
    return EOK;
337
}
338
 
2594 cejka 339
/** Register instance of device
340
 *
341
 */
342
static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
2619 jermar 343
    devmap_driver_t *driver)
2555 cejka 344
{
4345 svoboda 345
    if (driver == NULL) {
2619 jermar 346
        ipc_answer_0(iid, EREFUSED);
2594 cejka 347
        return;
348
    }
349
 
350
    /* Create new device entry */
4345 svoboda 351
    devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
352
    if (device == NULL) {
2619 jermar 353
        ipc_answer_0(iid, ENOMEM);
2594 cejka 354
        return;
355
    }
356
 
357
    /* Get device name */
4345 svoboda 358
    ipc_callid_t callid;
359
    size_t size;
2676 jermar 360
    if (!ipc_data_write_receive(&callid, &size)) {
2594 cejka 361
        free(device);
2619 jermar 362
        ipc_answer_0(iid, EREFUSED);
2594 cejka 363
        return;
364
    }
4345 svoboda 365
 
2594 cejka 366
    if (size > DEVMAP_NAME_MAXLEN) {
367
        free(device);
2619 jermar 368
        ipc_answer_0(callid, EINVAL);
369
        ipc_answer_0(iid, EREFUSED);
2594 cejka 370
        return;
371
    }
3150 svoboda 372
 
373
    /* +1 for terminating \0 */
374
    device->name = (char *) malloc(size + 1);
4345 svoboda 375
 
376
    if (device->name == NULL) {
2594 cejka 377
        free(device);
2619 jermar 378
        ipc_answer_0(callid, ENOMEM);
379
        ipc_answer_0(iid, EREFUSED);
2594 cejka 380
        return;
2555 cejka 381
    }
2594 cejka 382
 
2678 jermar 383
    ipc_data_write_finalize(callid, device->name, size);
2594 cejka 384
    device->name[size] = 0;
4345 svoboda 385
 
2594 cejka 386
    list_initialize(&(device->devices));
387
    list_initialize(&(device->driver_devices));
4345 svoboda 388
 
4691 svoboda 389
    fibril_mutex_lock(&devices_list_mutex);
4345 svoboda 390
 
2594 cejka 391
    /* Check that device with such name is not already registered */
392
    if (NULL != devmap_device_find_name(device->name)) {
3150 svoboda 393
        printf(NAME ": Device '%s' already registered\n", device->name);
4691 svoboda 394
        fibril_mutex_unlock(&devices_list_mutex);
2594 cejka 395
        free(device->name);
396
        free(device);
2619 jermar 397
        ipc_answer_0(iid, EEXISTS);
2594 cejka 398
        return;
2555 cejka 399
    }
4345 svoboda 400
 
2594 cejka 401
    /* Get unique device handle */
4345 svoboda 402
    device->handle = devmap_create_handle();
403
 
2594 cejka 404
    device->driver = driver;
2555 cejka 405
 
2594 cejka 406
    /* Insert device into list of all devices  */
2619 jermar 407
    list_append(&device->devices, &devices_list);
4345 svoboda 408
 
2594 cejka 409
    /* Insert device into list of devices that belog to one driver */
4691 svoboda 410
    fibril_mutex_lock(&device->driver->devices_mutex);
2594 cejka 411
 
2619 jermar 412
    list_append(&device->driver_devices, &device->driver->devices);
2594 cejka 413
 
4691 svoboda 414
    fibril_mutex_unlock(&device->driver->devices_mutex);
415
    fibril_condvar_broadcast(&devices_list_cv);
416
    fibril_mutex_unlock(&devices_list_mutex);
4345 svoboda 417
 
2619 jermar 418
    ipc_answer_1(iid, EOK, device->handle);
2555 cejka 419
}
420
 
2594 cejka 421
/**
422
 *
423
 */
424
static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
2619 jermar 425
    devmap_driver_t *driver)
2555 cejka 426
{
2594 cejka 427
    /* TODO */
2555 cejka 428
    return EOK;
429
}
430
 
2594 cejka 431
/** Connect client to the device.
4345 svoboda 432
 *
2594 cejka 433
 * Find device driver owning requested device and forward
434
 * the message to it.
4345 svoboda 435
 *
2547 cejka 436
 */
2594 cejka 437
static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
438
{
439
    /*
440
     * Get handle from request
441
     */
4691 svoboda 442
    dev_handle_t handle = IPC_GET_ARG2(*call);
4345 svoboda 443
    devmap_device_t *dev = devmap_device_find_handle(handle);
444
 
4691 svoboda 445
    if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) {
2619 jermar 446
        ipc_answer_0(callid, ENOENT);
2594 cejka 447
        return;
4345 svoboda 448
    }
449
 
4691 svoboda 450
    ipc_forward_fast(callid, dev->driver->phone, dev->handle,
2635 cejka 451
        IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
2594 cejka 452
}
453
 
454
/** Find handle for device instance identified by name.
4345 svoboda 455
 *
2594 cejka 456
 * In answer will be send EOK and device handle in arg1 or a error
4345 svoboda 457
 * code from errno.h.
458
 *
2594 cejka 459
 */
460
static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
461
{
4345 svoboda 462
    /*
2594 cejka 463
     * Wait for incoming message with device name (but do not
464
     * read the name itself until the buffer is allocated).
465
     */
4345 svoboda 466
    ipc_callid_t callid;
467
    size_t size;
468
    if (!ipc_data_write_receive(&callid, &size)) {
2619 jermar 469
        ipc_answer_0(callid, EREFUSED);
470
        ipc_answer_0(iid, EREFUSED);
2594 cejka 471
        return;
472
    }
4345 svoboda 473
 
474
    if ((size < 1) || (size > DEVMAP_NAME_MAXLEN)) {
2619 jermar 475
        ipc_answer_0(callid, EINVAL);
476
        ipc_answer_0(iid, EREFUSED);
2594 cejka 477
        return;
478
    }
4345 svoboda 479
 
2594 cejka 480
    /*
481
     * Allocate buffer for device name.
482
     */
4691 svoboda 483
    char *name = (char *) malloc(size + 1);
4345 svoboda 484
    if (name == NULL) {
2619 jermar 485
        ipc_answer_0(callid, ENOMEM);
486
        ipc_answer_0(iid, EREFUSED);
2594 cejka 487
        return;
4345 svoboda 488
    }
489
 
2594 cejka 490
    /*
491
     * Send confirmation to sender and get data into buffer.
492
     */
4345 svoboda 493
    ipcarg_t retval = ipc_data_write_finalize(callid, name, size);
494
    if (retval != EOK) {
2619 jermar 495
        ipc_answer_0(iid, EREFUSED);
4345 svoboda 496
        free(name);
2594 cejka 497
        return;
498
    }
4345 svoboda 499
    name[size] = '\0';
500
 
4691 svoboda 501
    fibril_mutex_lock(&devices_list_mutex);
502
    const devmap_device_t *dev;
503
recheck:
504
 
2594 cejka 505
    /*
4691 svoboda 506
     * Find device name in the list of known devices.
2594 cejka 507
     */
4691 svoboda 508
    dev = devmap_device_find_name(name);
4345 svoboda 509
 
2594 cejka 510
    /*
511
     * Device was not found.
512
     */
4345 svoboda 513
    if (dev == NULL) {
514
        if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
4691 svoboda 515
            /* Blocking lookup */
516
            fibril_condvar_wait(&devices_list_cv,
517
                &devices_list_mutex);
518
            goto recheck;
4345 svoboda 519
        }
520
 
2619 jermar 521
        ipc_answer_0(iid, ENOENT);
4345 svoboda 522
        free(name);
4691 svoboda 523
        fibril_mutex_unlock(&devices_list_mutex);
2594 cejka 524
        return;
525
    }
4691 svoboda 526
    fibril_mutex_unlock(&devices_list_mutex);
4345 svoboda 527
 
2619 jermar 528
    ipc_answer_1(iid, EOK, dev->handle);
4345 svoboda 529
    free(name);
2594 cejka 530
}
531
 
4345 svoboda 532
/** Find name of device identified by id and send it to caller.
2594 cejka 533
 *
534
 */
4691 svoboda 535
static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
2594 cejka 536
{
4345 svoboda 537
    const devmap_device_t *device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
538
 
2594 cejka 539
    /*
540
     * Device not found.
541
     */
4345 svoboda 542
    if (device == NULL) {
2619 jermar 543
        ipc_answer_0(iid, ENOENT);
2594 cejka 544
        return;
4345 svoboda 545
    }
546
 
2619 jermar 547
    ipc_answer_0(iid, EOK);
4345 svoboda 548
 
549
    /* FIXME:
550
     * We have no channel from DEVMAP to client, therefore
551
     * sending must be initiated by client.
552
     *
4691 svoboda 553
     * size_t name_size = str_size(device->name);
554
     *
4345 svoboda 555
     * int rc = ipc_data_write_send(phone, device->name, name_size);
556
     * if (rc != EOK) {
557
     *     async_wait_for(req, NULL);
558
     *     return rc;
559
     * }
560
     */
561
 
2594 cejka 562
    /* TODO: send name in response */
563
}
564
 
4691 svoboda 565
static void devmap_get_count(ipc_callid_t iid, ipc_call_t *icall)
566
{
567
    fibril_mutex_lock(&devices_list_mutex);
568
    ipc_answer_1(iid, EOK, list_count(&devices_list));
569
    fibril_mutex_unlock(&devices_list_mutex);
570
}
571
 
572
static void devmap_get_devices(ipc_callid_t iid, ipc_call_t *icall)
573
{
574
    fibril_mutex_lock(&devices_list_mutex);
575
 
576
    ipc_callid_t callid;
577
    size_t size;
578
    if (!ipc_data_read_receive(&callid, &size)) {
579
        ipc_answer_0(callid, EREFUSED);
580
        ipc_answer_0(iid, EREFUSED);
581
        return;
582
    }
583
 
584
    if ((size % sizeof(dev_desc_t)) != 0) {
585
        ipc_answer_0(callid, EINVAL);
586
        ipc_answer_0(iid, EREFUSED);
587
        return;
588
    }
589
 
590
    size_t count = size / sizeof(dev_desc_t);
591
    dev_desc_t *desc = (dev_desc_t *) malloc(size);
592
    if (desc == NULL) {
593
        ipc_answer_0(callid, ENOMEM);
594
        ipc_answer_0(iid, EREFUSED);
595
        return;
596
    }
597
 
598
    size_t pos = 0;
599
    link_t *item = devices_list.next;
600
 
601
    while ((item != &devices_list) && (pos < count)) {
602
        devmap_device_t *device = list_get_instance(item, devmap_device_t, devices);
603
 
604
        desc[pos].handle = device->handle;
605
        str_cpy(desc[pos].name, DEVMAP_NAME_MAXLEN, device->name);
606
        pos++;
607
        item = item->next;
608
    }
609
 
610
    ipcarg_t retval = ipc_data_read_finalize(callid, desc, pos * sizeof(dev_desc_t));
611
    if (retval != EOK) {
612
        ipc_answer_0(iid, EREFUSED);
613
        free(desc);
614
        return;
615
    }
616
 
617
    free(desc);
618
 
619
    fibril_mutex_unlock(&devices_list_mutex);
620
 
621
    ipc_answer_1(iid, EOK, pos);
622
}
623
 
624
static void devmap_null_create(ipc_callid_t iid, ipc_call_t *icall)
625
{
626
    fibril_mutex_lock(&null_devices_mutex);
627
 
628
    unsigned int i;
629
    bool fnd = false;
630
 
631
    for (i = 0; i < NULL_DEVICES; i++) {
632
        if (null_devices[i] == NULL) {
633
            fnd = true;
634
            break;
635
        }
636
    }
637
 
638
    if (!fnd) {
639
        fibril_mutex_unlock(&null_devices_mutex);
640
        ipc_answer_0(iid, ENOMEM);
641
        return;
642
    }
643
 
644
    /* Create NULL device entry */
645
    devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
646
    if (device == NULL) {
647
        fibril_mutex_unlock(&null_devices_mutex);
648
        ipc_answer_0(iid, ENOMEM);
649
        return;
650
    }
651
 
652
    char null[DEVMAP_NAME_MAXLEN];
653
    snprintf(null, DEVMAP_NAME_MAXLEN, "null%u", i);
654
 
655
    device->name = str_dup(null);
656
    if (device->name == NULL) {
657
        fibril_mutex_unlock(&null_devices_mutex);
658
        free(device);
659
        ipc_answer_0(iid, ENOMEM);
660
        return;
661
    }
662
 
663
    list_initialize(&(device->devices));
664
    list_initialize(&(device->driver_devices));
665
 
666
    fibril_mutex_lock(&devices_list_mutex);
667
 
668
    /* Get unique device handle */
669
    device->handle = devmap_create_handle();
670
    device->driver = NULL;
671
 
672
    /* Insert device into list of all devices
673
       and into null devices array */
674
    list_append(&device->devices, &devices_list);
675
    null_devices[i] = device;
676
 
677
    fibril_mutex_unlock(&devices_list_mutex);
678
    fibril_mutex_unlock(&null_devices_mutex);
679
 
680
    ipc_answer_1(iid, EOK, (ipcarg_t) i);
681
}
682
 
683
static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
684
{
685
    fibril_mutex_lock(&null_devices_mutex);
686
 
687
    ipcarg_t i = IPC_GET_ARG1(*icall);
688
 
689
    if (null_devices[i] == NULL) {
690
        ipc_answer_0(iid, ENOENT);
691
        return;
692
    }
693
 
694
    devmap_device_unregister_core(null_devices[i]);
695
    null_devices[i] = NULL;
696
 
697
    fibril_mutex_unlock(&null_devices_mutex);
698
 
699
    ipc_answer_0(iid, EOK);
700
}
701
 
702
/** Initialize device mapper.
703
 *
704
 *
705
 */
706
static bool devmap_init(void)
707
{
708
    fibril_mutex_lock(&null_devices_mutex);
709
 
710
    unsigned int i;
711
    for (i = 0; i < NULL_DEVICES; i++)
712
        null_devices[i] = NULL;
713
 
714
    fibril_mutex_unlock(&null_devices_mutex);
715
 
716
    return true;
717
}
718
 
2594 cejka 719
/** Handle connection with device driver.
720
 *
721
 */
3150 svoboda 722
static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
2547 cejka 723
{
4345 svoboda 724
    /* Accept connection */
725
    ipc_answer_0(iid, EOK);
726
 
4691 svoboda 727
    devmap_driver_t *driver = NULL;
2594 cejka 728
    devmap_driver_register(&driver);
4345 svoboda 729
 
3150 svoboda 730
    if (NULL == driver)
2594 cejka 731
        return;
732
 
4345 svoboda 733
    bool cont = true;
2555 cejka 734
    while (cont) {
4345 svoboda 735
        ipc_call_t call;
736
        ipc_callid_t callid = async_get_call(&call);
737
 
738
        switch (IPC_GET_METHOD(call)) {
2547 cejka 739
        case IPC_M_PHONE_HUNGUP:
2555 cejka 740
            cont = false;
4345 svoboda 741
            continue;
2594 cejka 742
        case DEVMAP_DRIVER_UNREGISTER:
4345 svoboda 743
            if (NULL == driver)
2619 jermar 744
                ipc_answer_0(callid, ENOENT);
4345 svoboda 745
            else
2619 jermar 746
                ipc_answer_0(callid, EOK);
2555 cejka 747
            break;
2594 cejka 748
        case DEVMAP_DEVICE_REGISTER:
749
            /* Register one instance of device */
750
            devmap_device_register(callid, &call, driver);
2555 cejka 751
            break;
2594 cejka 752
        case DEVMAP_DEVICE_UNREGISTER:
753
            /* Remove instance of device identified by handler */
754
            devmap_device_unregister(callid, &call, driver);
755
            break;
756
        case DEVMAP_DEVICE_GET_HANDLE:
757
            devmap_get_handle(callid, &call);
758
            break;
759
        case DEVMAP_DEVICE_GET_NAME:
4691 svoboda 760
            devmap_get_name(callid, &call);
2594 cejka 761
            break;
762
        default:
4345 svoboda 763
            if (!(callid & IPC_CALLID_NOTIFICATION))
2619 jermar 764
                ipc_answer_0(callid, ENOENT);
2594 cejka 765
        }
766
    }
767
 
4691 svoboda 768
    if (driver != NULL) {
4345 svoboda 769
        /*
2619 jermar 770
         * Unregister the device driver and all its devices.
771
         */
2594 cejka 772
        devmap_driver_unregister(driver);
773
        driver = NULL;
774
    }
775
}
776
 
777
/** Handle connection with device client.
778
 *
779
 */
3150 svoboda 780
static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
2594 cejka 781
{
4345 svoboda 782
    /* Accept connection */
783
    ipc_answer_0(iid, EOK);
784
 
2594 cejka 785
    bool cont = true;
786
    while (cont) {
4345 svoboda 787
        ipc_call_t call;
788
        ipc_callid_t callid = async_get_call(&call);
789
 
790
        switch (IPC_GET_METHOD(call)) {
2594 cejka 791
        case IPC_M_PHONE_HUNGUP:
2555 cejka 792
            cont = false;
4345 svoboda 793
            continue;
2594 cejka 794
        case DEVMAP_DEVICE_GET_HANDLE:
4345 svoboda 795
            devmap_get_handle(callid, &call);
2555 cejka 796
            break;
2594 cejka 797
        case DEVMAP_DEVICE_GET_NAME:
798
            devmap_get_name(callid, &call);
799
            break;
4691 svoboda 800
        case DEVMAP_DEVICE_NULL_CREATE:
801
            devmap_null_create(callid, &call);
802
            break;
803
        case DEVMAP_DEVICE_NULL_DESTROY:
804
            devmap_null_destroy(callid, &call);
805
            break;
806
        case DEVMAP_DEVICE_GET_COUNT:
807
            devmap_get_count(callid, &call);
808
            break;
809
        case DEVMAP_DEVICE_GET_DEVICES:
810
            devmap_get_devices(callid, &call);
811
            break;
2547 cejka 812
        default:
4345 svoboda 813
            if (!(callid & IPC_CALLID_NOTIFICATION))
2619 jermar 814
                ipc_answer_0(callid, ENOENT);
2547 cejka 815
        }
816
    }
2594 cejka 817
}
818
 
4345 svoboda 819
/** Function for handling connections to devmap
2594 cejka 820
 *
821
 */
3150 svoboda 822
static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
2594 cejka 823
{
3150 svoboda 824
    /* Select interface */
825
    switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
2598 jermar 826
    case DEVMAP_DRIVER:
827
        devmap_connection_driver(iid, icall);
828
        break;
829
    case DEVMAP_CLIENT:
830
        devmap_connection_client(iid, icall);
831
        break;
2635 cejka 832
    case DEVMAP_CONNECT_TO_DEVICE:
3150 svoboda 833
        /* Connect client to selected device */
2635 cejka 834
        devmap_forward(iid, icall);
835
        break;
2598 jermar 836
    default:
4345 svoboda 837
        /* No such interface */
4691 svoboda 838
        ipc_answer_0(iid, ENOENT);
2594 cejka 839
    }
2547 cejka 840
}
841
 
2594 cejka 842
/**
843
 *
844
 */
2547 cejka 845
int main(int argc, char *argv[])
846
{
3150 svoboda 847
    printf(NAME ": HelenOS Device Mapper\n");
848
 
4691 svoboda 849
    if (!devmap_init()) {
3150 svoboda 850
        printf(NAME ": Error while initializing service\n");
2547 cejka 851
        return -1;
852
    }
3150 svoboda 853
 
854
    /* Set a handler of incomming connections */
2594 cejka 855
    async_set_client_connection(devmap_connection);
4345 svoboda 856
 
2598 jermar 857
    /* Register device mapper at naming service */
4345 svoboda 858
    ipcarg_t phonead;
2637 cejka 859
    if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
2547 cejka 860
        return -1;
861
 
3150 svoboda 862
    printf(NAME ": Accepting connections\n");
2547 cejka 863
    async_manager();
4345 svoboda 864
 
2547 cejka 865
    /* Never reached */
866
    return 0;
867
}
868
 
869
/**
870
 * @}
871
 */