~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/devmap/devmap.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2007 Josef Cejka
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * - Redistributions of source code must retain the above copyright
 
10
 *   notice, this list of conditions and the following disclaimer.
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 *   notice, this list of conditions and the following disclaimer in the
 
13
 *   documentation and/or other materials provided with the distribution.
 
14
 * - The name of the author may not be used to endorse or promote products
 
15
 *   derived from this software without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 */
 
28
 
 
29
/**
 
30
 * @defgroup devmap Device mapper.
 
31
 * @brief HelenOS device mapper.
 
32
 * @{
 
33
 */
 
34
 
 
35
/** @file
 
36
 */
 
37
 
 
38
#include <ipc/services.h>
 
39
#include <ipc/ns.h>
 
40
#include <async.h>
 
41
#include <stdio.h>
 
42
#include <errno.h>
 
43
#include <bool.h>
 
44
#include <fibril_sync.h>
 
45
#include <stdlib.h>
 
46
#include <string.h>
 
47
#include <ipc/devmap.h>
 
48
 
 
49
#define NAME          "devmap"
 
50
#define NULL_DEVICES  256
 
51
 
 
52
/** Representation of device driver.
 
53
 *
 
54
 * Each driver is responsible for a set of devices.
 
55
 *
 
56
 */
 
57
typedef 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;
 
66
        /** Fibril mutex for list of devices owned by this driver */
 
67
        fibril_mutex_t devices_mutex;
 
68
} devmap_driver_t;
 
69
 
 
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
 
 
87
LIST_INITIALIZE(devices_list);
 
88
LIST_INITIALIZE(drivers_list);
 
89
 
 
90
/* Locking order:
 
91
 *  drivers_list_mutex
 
92
 *  devices_list_mutex
 
93
 *  (devmap_driver_t *)->devices_mutex
 
94
 *  create_handle_mutex
 
95
 **/
 
96
 
 
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);
 
102
 
 
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)
 
107
{
 
108
        /* TODO: allow reusing old handles after their unregistration
 
109
         * and implement some version of LRU algorithm, avoid overflow
 
110
         */
 
111
        
 
112
        fibril_mutex_lock(&create_handle_mutex);
 
113
        last_handle++;
 
114
        fibril_mutex_unlock(&create_handle_mutex);
 
115
        
 
116
        return last_handle;
 
117
}
 
118
 
 
119
/** Find device with given name.
 
120
 *
 
121
 */
 
122
static devmap_device_t *devmap_device_find_name(const char *name)
 
123
{
 
124
        link_t *item = devices_list.next;
 
125
        devmap_device_t *device = NULL;
 
126
        
 
127
        while (item != &devices_list) {
 
128
                device = list_get_instance(item, devmap_device_t, devices);
 
129
                if (str_cmp(device->name, name) == 0)
 
130
                        break;
 
131
                item = item->next;
 
132
        }
 
133
        
 
134
        if (item == &devices_list)
 
135
                return NULL;
 
136
        
 
137
        device = list_get_instance(item, devmap_device_t, devices);
 
138
        return device;
 
139
}
 
140
 
 
141
/** Find device with given handle.
 
142
 *
 
143
 * @todo: use hash table
 
144
 *
 
145
 */
 
146
static devmap_device_t *devmap_device_find_handle(dev_handle_t handle)
 
147
{
 
148
        fibril_mutex_lock(&devices_list_mutex);
 
149
        
 
150
        link_t *item = (&devices_list)->next;
 
151
        devmap_device_t *device = NULL;
 
152
        
 
153
        while (item != &devices_list) {
 
154
                device = list_get_instance(item, devmap_device_t, devices);
 
155
                if (device->handle == handle)
 
156
                        break;
 
157
                item = item->next;
 
158
        }
 
159
        
 
160
        if (item == &devices_list) {
 
161
                fibril_mutex_unlock(&devices_list_mutex);
 
162
                return NULL;
 
163
        }
 
164
        
 
165
        device = list_get_instance(item, devmap_device_t, devices);
 
166
        
 
167
        fibril_mutex_unlock(&devices_list_mutex);
 
168
        
 
169
        return device;
 
170
}
 
171
 
 
172
/**
 
173
 * Unregister device and free it. It's assumed that driver's device list is
 
174
 * already locked.
 
175
 */
 
176
static int devmap_device_unregister_core(devmap_device_t *device)
 
177
{
 
178
        list_remove(&(device->devices));
 
179
        list_remove(&(device->driver_devices));
 
180
        
 
181
        free(device->name);
 
182
        free(device);
 
183
        
 
184
        return EOK;
 
185
}
 
186
 
 
187
/**
 
188
 * Read info about new driver and add it into linked list of registered
 
189
 * drivers.
 
190
 */
 
191
static void devmap_driver_register(devmap_driver_t **odriver)
 
192
{
 
193
        *odriver = NULL;
 
194
        
 
195
        ipc_call_t icall;
 
196
        ipc_callid_t iid = async_get_call(&icall);
 
197
        
 
198
        if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
 
199
                ipc_answer_0(iid, EREFUSED);
 
200
                return;
 
201
        }
 
202
        
 
203
        devmap_driver_t *driver = (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
 
204
        
 
205
        if (driver == NULL) {
 
206
                ipc_answer_0(iid, ENOMEM);
 
207
                return;
 
208
        }
 
209
        
 
210
        /*
 
211
         * Get driver name
 
212
         */
 
213
        ipc_callid_t callid;
 
214
        size_t name_size;
 
215
        if (!ipc_data_write_receive(&callid, &name_size)) {
 
216
                free(driver);
 
217
                ipc_answer_0(callid, EREFUSED);
 
218
                ipc_answer_0(iid, EREFUSED);
 
219
                return;
 
220
        }
 
221
        
 
222
        if (name_size > DEVMAP_NAME_MAXLEN) {
 
223
                free(driver);
 
224
                ipc_answer_0(callid, EINVAL);
 
225
                ipc_answer_0(iid, EREFUSED);
 
226
                return;
 
227
        }
 
228
        
 
229
        /*
 
230
         * Allocate buffer for device name.
 
231
         */
 
232
        driver->name = (char *) malloc(name_size + 1);
 
233
        if (driver->name == NULL) {
 
234
                free(driver);
 
235
                ipc_answer_0(callid, ENOMEM);
 
236
                ipc_answer_0(iid, EREFUSED);
 
237
                return;
 
238
        }
 
239
        
 
240
        /*
 
241
         * Send confirmation to sender and get data into buffer.
 
242
         */
 
243
        if (ipc_data_write_finalize(callid, driver->name, name_size) != EOK) {
 
244
                free(driver->name);
 
245
                free(driver);
 
246
                ipc_answer_0(iid, EREFUSED);
 
247
                return;
 
248
        }
 
249
        
 
250
        driver->name[name_size] = 0;
 
251
        
 
252
        /* Initialize mutex for list of devices owned by this driver */
 
253
        fibril_mutex_initialize(&driver->devices_mutex);
 
254
        
 
255
        /*
 
256
         * Initialize list of asociated devices
 
257
         */
 
258
        list_initialize(&driver->devices);
 
259
        
 
260
        /*
 
261
         * Create connection to the driver
 
262
         */
 
263
        ipc_call_t call;
 
264
        callid = async_get_call(&call);
 
265
        
 
266
        if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
 
267
                ipc_answer_0(callid, ENOTSUP);
 
268
                
 
269
                free(driver->name);
 
270
                free(driver);
 
271
                ipc_answer_0(iid, ENOTSUP);
 
272
                return;
 
273
        }
 
274
        
 
275
        driver->phone = IPC_GET_ARG5(call);
 
276
        
 
277
        ipc_answer_0(callid, EOK);
 
278
        
 
279
        list_initialize(&(driver->drivers));
 
280
        
 
281
        fibril_mutex_lock(&drivers_list_mutex);
 
282
        
 
283
        /* TODO:
 
284
         * check that no driver with name equal to driver->name is registered
 
285
         */
 
286
        
 
287
        /*
 
288
         * Insert new driver into list of registered drivers
 
289
         */
 
290
        list_append(&(driver->drivers), &drivers_list);
 
291
        fibril_mutex_unlock(&drivers_list_mutex);
 
292
        
 
293
        ipc_answer_0(iid, EOK);
 
294
        
 
295
        *odriver = driver;
 
296
}
 
297
 
 
298
/**
 
299
 * Unregister device driver, unregister all its devices and free driver
 
300
 * structure.
 
301
 *
 
302
 */
 
303
static int devmap_driver_unregister(devmap_driver_t *driver)
 
304
{
 
305
        if (driver == NULL)
 
306
                return EEXISTS;
 
307
        
 
308
        fibril_mutex_lock(&drivers_list_mutex);
 
309
        
 
310
        if (driver->phone != 0)
 
311
                ipc_hangup(driver->phone);
 
312
        
 
313
        /* Remove it from list of drivers */
 
314
        list_remove(&(driver->drivers));
 
315
        
 
316
        /* Unregister all its devices */
 
317
        fibril_mutex_lock(&devices_list_mutex);
 
318
        fibril_mutex_lock(&driver->devices_mutex);
 
319
        
 
320
        while (!list_empty(&(driver->devices))) {
 
321
                devmap_device_t *device = list_get_instance(driver->devices.next,
 
322
                    devmap_device_t, driver_devices);
 
323
                devmap_device_unregister_core(device);
 
324
        }
 
325
        
 
326
        fibril_mutex_unlock(&driver->devices_mutex);
 
327
        fibril_mutex_unlock(&devices_list_mutex);
 
328
        fibril_mutex_unlock(&drivers_list_mutex);
 
329
        
 
330
        /* free name and driver */
 
331
        if (driver->name != NULL)
 
332
                free(driver->name);
 
333
        
 
334
        free(driver);
 
335
        
 
336
        return EOK;
 
337
}
 
338
 
 
339
/** Register instance of device
 
340
 *
 
341
 */
 
342
static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
 
343
    devmap_driver_t *driver)
 
344
{
 
345
        if (driver == NULL) {
 
346
                ipc_answer_0(iid, EREFUSED);
 
347
                return;
 
348
        }
 
349
        
 
350
        /* Create new device entry */
 
351
        devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
 
352
        if (device == NULL) {
 
353
                ipc_answer_0(iid, ENOMEM);
 
354
                return;
 
355
        }
 
356
        
 
357
        /* Get device name */
 
358
        ipc_callid_t callid;
 
359
        size_t size;
 
360
        if (!ipc_data_write_receive(&callid, &size)) {
 
361
                free(device);
 
362
                ipc_answer_0(iid, EREFUSED);
 
363
                return;
 
364
        }
 
365
        
 
366
        if (size > DEVMAP_NAME_MAXLEN) {
 
367
                free(device);
 
368
                ipc_answer_0(callid, EINVAL);
 
369
                ipc_answer_0(iid, EREFUSED);
 
370
                return;
 
371
        }
 
372
        
 
373
        /* +1 for terminating \0 */
 
374
        device->name = (char *) malloc(size + 1);
 
375
        
 
376
        if (device->name == NULL) {
 
377
                free(device);
 
378
                ipc_answer_0(callid, ENOMEM);
 
379
                ipc_answer_0(iid, EREFUSED);
 
380
                return;
 
381
        }
 
382
        
 
383
        ipc_data_write_finalize(callid, device->name, size);
 
384
        device->name[size] = 0;
 
385
        
 
386
        list_initialize(&(device->devices));
 
387
        list_initialize(&(device->driver_devices));
 
388
        
 
389
        fibril_mutex_lock(&devices_list_mutex);
 
390
        
 
391
        /* Check that device with such name is not already registered */
 
392
        if (NULL != devmap_device_find_name(device->name)) {
 
393
                printf(NAME ": Device '%s' already registered\n", device->name);
 
394
                fibril_mutex_unlock(&devices_list_mutex);
 
395
                free(device->name);
 
396
                free(device);
 
397
                ipc_answer_0(iid, EEXISTS);
 
398
                return;
 
399
        }
 
400
        
 
401
        /* Get unique device handle */
 
402
        device->handle = devmap_create_handle();
 
403
        
 
404
        device->driver = driver;
 
405
        
 
406
        /* Insert device into list of all devices  */
 
407
        list_append(&device->devices, &devices_list);
 
408
        
 
409
        /* Insert device into list of devices that belog to one driver */
 
410
        fibril_mutex_lock(&device->driver->devices_mutex);
 
411
        
 
412
        list_append(&device->driver_devices, &device->driver->devices);
 
413
        
 
414
        fibril_mutex_unlock(&device->driver->devices_mutex);
 
415
        fibril_condvar_broadcast(&devices_list_cv);
 
416
        fibril_mutex_unlock(&devices_list_mutex);
 
417
        
 
418
        ipc_answer_1(iid, EOK, device->handle);
 
419
}
 
420
 
 
421
/**
 
422
 *
 
423
 */
 
424
static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall, 
 
425
    devmap_driver_t *driver)
 
426
{
 
427
        /* TODO */
 
428
        return EOK;
 
429
}
 
430
 
 
431
/** Connect client to the device.
 
432
 *
 
433
 * Find device driver owning requested device and forward
 
434
 * the message to it.
 
435
 *
 
436
 */
 
437
static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
 
438
{
 
439
        /*
 
440
         * Get handle from request
 
441
         */
 
442
        dev_handle_t handle = IPC_GET_ARG2(*call);
 
443
        devmap_device_t *dev = devmap_device_find_handle(handle);
 
444
        
 
445
        if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) {
 
446
                ipc_answer_0(callid, ENOENT);
 
447
                return;
 
448
        }
 
449
        
 
450
        ipc_forward_fast(callid, dev->driver->phone, dev->handle,
 
451
            IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
 
452
}
 
453
 
 
454
/** Find handle for device instance identified by name.
 
455
 *
 
456
 * In answer will be send EOK and device handle in arg1 or a error
 
457
 * code from errno.h.
 
458
 *
 
459
 */
 
460
static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
 
461
{
 
462
        /*
 
463
         * Wait for incoming message with device name (but do not
 
464
         * read the name itself until the buffer is allocated).
 
465
         */
 
466
        ipc_callid_t callid;
 
467
        size_t size;
 
468
        if (!ipc_data_write_receive(&callid, &size)) {
 
469
                ipc_answer_0(callid, EREFUSED);
 
470
                ipc_answer_0(iid, EREFUSED);
 
471
                return;
 
472
        }
 
473
        
 
474
        if ((size < 1) || (size > DEVMAP_NAME_MAXLEN)) {
 
475
                ipc_answer_0(callid, EINVAL);
 
476
                ipc_answer_0(iid, EREFUSED);
 
477
                return;
 
478
        }
 
479
        
 
480
        /*
 
481
         * Allocate buffer for device name.
 
482
         */
 
483
        char *name = (char *) malloc(size + 1);
 
484
        if (name == NULL) {
 
485
                ipc_answer_0(callid, ENOMEM);
 
486
                ipc_answer_0(iid, EREFUSED);
 
487
                return;
 
488
        }
 
489
        
 
490
        /*
 
491
         * Send confirmation to sender and get data into buffer.
 
492
         */
 
493
        ipcarg_t retval = ipc_data_write_finalize(callid, name, size);
 
494
        if (retval != EOK) {
 
495
                ipc_answer_0(iid, EREFUSED);
 
496
                free(name);
 
497
                return;
 
498
        }
 
499
        name[size] = '\0';
 
500
        
 
501
        fibril_mutex_lock(&devices_list_mutex);
 
502
        const devmap_device_t *dev;
 
503
recheck:
 
504
 
 
505
        /*
 
506
         * Find device name in the list of known devices.
 
507
         */
 
508
        dev = devmap_device_find_name(name);
 
509
        
 
510
        /*
 
511
         * Device was not found.
 
512
         */
 
513
        if (dev == NULL) {
 
514
                if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
 
515
                        /* Blocking lookup */
 
516
                        fibril_condvar_wait(&devices_list_cv,
 
517
                            &devices_list_mutex);
 
518
                        goto recheck;
 
519
                }
 
520
                
 
521
                ipc_answer_0(iid, ENOENT);
 
522
                free(name);
 
523
                fibril_mutex_unlock(&devices_list_mutex);
 
524
                return;
 
525
        }
 
526
        fibril_mutex_unlock(&devices_list_mutex);
 
527
        
 
528
        ipc_answer_1(iid, EOK, dev->handle);
 
529
        free(name);
 
530
}
 
531
 
 
532
/** Find name of device identified by id and send it to caller.
 
533
 *
 
534
 */
 
535
static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
 
536
{
 
537
        const devmap_device_t *device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
 
538
        
 
539
        /*
 
540
         * Device not found.
 
541
         */
 
542
        if (device == NULL) {
 
543
                ipc_answer_0(iid, ENOENT);
 
544
                return;
 
545
        }
 
546
        
 
547
        ipc_answer_0(iid, EOK);
 
548
        
 
549
        /* FIXME:
 
550
         * We have no channel from DEVMAP to client, therefore
 
551
         * sending must be initiated by client.
 
552
         *
 
553
         * size_t name_size = str_size(device->name);
 
554
         *
 
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
        
 
562
        /* TODO: send name in response */
 
563
}
 
564
 
 
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
 
 
719
/** Handle connection with device driver.
 
720
 *
 
721
 */
 
722
static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
 
723
{
 
724
        /* Accept connection */
 
725
        ipc_answer_0(iid, EOK);
 
726
        
 
727
        devmap_driver_t *driver = NULL;
 
728
        devmap_driver_register(&driver);
 
729
        
 
730
        if (NULL == driver)
 
731
                return;
 
732
        
 
733
        bool cont = true;
 
734
        while (cont) {
 
735
                ipc_call_t call;
 
736
                ipc_callid_t callid = async_get_call(&call);
 
737
                
 
738
                switch (IPC_GET_METHOD(call)) {
 
739
                case IPC_M_PHONE_HUNGUP:
 
740
                        cont = false;
 
741
                        continue;
 
742
                case DEVMAP_DRIVER_UNREGISTER:
 
743
                        if (NULL == driver)
 
744
                                ipc_answer_0(callid, ENOENT);
 
745
                        else
 
746
                                ipc_answer_0(callid, EOK);
 
747
                        break;
 
748
                case DEVMAP_DEVICE_REGISTER:
 
749
                        /* Register one instance of device */
 
750
                        devmap_device_register(callid, &call, driver);
 
751
                        break;
 
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:
 
760
                        devmap_get_name(callid, &call);
 
761
                        break;
 
762
                default:
 
763
                        if (!(callid & IPC_CALLID_NOTIFICATION))
 
764
                                ipc_answer_0(callid, ENOENT);
 
765
                }
 
766
        }
 
767
        
 
768
        if (driver != NULL) {
 
769
                /*
 
770
                 * Unregister the device driver and all its devices.
 
771
                 */
 
772
                devmap_driver_unregister(driver);
 
773
                driver = NULL;
 
774
        }
 
775
}
 
776
 
 
777
/** Handle connection with device client.
 
778
 *
 
779
 */
 
780
static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
 
781
{
 
782
        /* Accept connection */
 
783
        ipc_answer_0(iid, EOK);
 
784
        
 
785
        bool cont = true;
 
786
        while (cont) {
 
787
                ipc_call_t call;
 
788
                ipc_callid_t callid = async_get_call(&call);
 
789
                
 
790
                switch (IPC_GET_METHOD(call)) {
 
791
                case IPC_M_PHONE_HUNGUP:
 
792
                        cont = false;
 
793
                        continue;
 
794
                case DEVMAP_DEVICE_GET_HANDLE:
 
795
                        devmap_get_handle(callid, &call);
 
796
                        break;
 
797
                case DEVMAP_DEVICE_GET_NAME:
 
798
                        devmap_get_name(callid, &call);
 
799
                        break;
 
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;
 
812
                default:
 
813
                        if (!(callid & IPC_CALLID_NOTIFICATION))
 
814
                                ipc_answer_0(callid, ENOENT);
 
815
                }
 
816
        }
 
817
}
 
818
 
 
819
/** Function for handling connections to devmap
 
820
 *
 
821
 */
 
822
static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
 
823
{
 
824
        /* Select interface */
 
825
        switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
 
826
        case DEVMAP_DRIVER:
 
827
                devmap_connection_driver(iid, icall);
 
828
                break;
 
829
        case DEVMAP_CLIENT:
 
830
                devmap_connection_client(iid, icall);
 
831
                break;
 
832
        case DEVMAP_CONNECT_TO_DEVICE:
 
833
                /* Connect client to selected device */
 
834
                devmap_forward(iid, icall);
 
835
                break;
 
836
        default:
 
837
                /* No such interface */
 
838
                ipc_answer_0(iid, ENOENT);
 
839
        }
 
840
}
 
841
 
 
842
/**
 
843
 *
 
844
 */
 
845
int main(int argc, char *argv[])
 
846
{
 
847
        printf(NAME ": HelenOS Device Mapper\n");
 
848
        
 
849
        if (!devmap_init()) {
 
850
                printf(NAME ": Error while initializing service\n");
 
851
                return -1;
 
852
        }
 
853
        
 
854
        /* Set a handler of incomming connections */
 
855
        async_set_client_connection(devmap_connection);
 
856
        
 
857
        /* Register device mapper at naming service */
 
858
        ipcarg_t phonead;
 
859
        if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0) 
 
860
                return -1;
 
861
        
 
862
        printf(NAME ": Accepting connections\n");
 
863
        async_manager();
 
864
        
 
865
        /* Never reached */
 
866
        return 0;
 
867
}
 
868
 
 
869
/** 
 
870
 * @}
 
871
 */