Project

General

Profile

Statistics
| Branch: | Revision:

root / prex-0.9.0 / sys / kern / device.c @ 03e9c04a

History | View | Annotate | Download (12.2 KB)

1 03e9c04a Brad Neuman
/*-
2
 * Copyright (c) 2005-2009, Kohsuke Ohtani
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
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
/*
31
 * device.c - device I/O support routines
32
 */
33
34
/**
35
 * The device_* system calls are interfaces to access the specific
36
 * device object which is handled by the related device driver.
37
 *
38
 * The routines in this moduile have the following role:
39
 *  - Manage the name space for device objects.
40
 *  - Forward user I/O requests to the drivers with minimum check.
41
 *  - Provide the table for the Driver-Kernel Interface.
42
 */
43
44
#include <kernel.h>
45
#include <irq.h>
46
#include <kmem.h>
47
#include <task.h>
48
#include <timer.h>
49
#include <page.h>
50
#include <sched.h>
51
#include <exception.h>
52
#include <vm.h>
53
#include <device.h>
54
#include <system.h>
55
#include <hal.h>
56
57
/* forward declarations */
58
static device_t        device_create(struct driver *, const char *, int);
59
static int        device_destroy(device_t);
60
static device_t        device_lookup(const char *);
61
static int        device_valid(device_t);
62
static int        device_reference(device_t);
63
static void        device_release(device_t);
64
static void        *device_private(device_t);
65
static int        device_control(device_t, u_long, void *);
66
static int        device_broadcast(u_long, void *, int);
67
68
#define DKIENT(func)        (dkifn_t)(func)
69
70
/*
71
 * Driver-Kernel Interface (DKI)
72
 */
73
static const dkifn_t dkient[] = {
74
        /*  0 */ DKIENT(copyin),
75
        /*  1 */ DKIENT(copyout),
76
        /*  2 */ DKIENT(copyinstr),
77
        /*  3 */ DKIENT(kmem_alloc),
78
        /*  4 */ DKIENT(kmem_free),
79
        /*  5 */ DKIENT(kmem_map),
80
        /*  6 */ DKIENT(page_alloc),
81
        /*  7 */ DKIENT(page_free),
82
        /*  8 */ DKIENT(page_reserve),
83
        /*  9 */ DKIENT(irq_attach),
84
        /* 10 */ DKIENT(irq_detach),
85
        /* 11 */ DKIENT(spl0),
86
        /* 12 */ DKIENT(splhigh),
87
        /* 13 */ DKIENT(splx),
88
        /* 14 */ DKIENT(timer_callout),
89
        /* 15 */ DKIENT(timer_stop),
90
        /* 16 */ DKIENT(timer_delay),
91
        /* 17 */ DKIENT(timer_ticks),
92
        /* 18 */ DKIENT(sched_lock),
93
        /* 19 */ DKIENT(sched_unlock),
94
        /* 20 */ DKIENT(sched_tsleep),
95
        /* 21 */ DKIENT(sched_wakeup),
96
        /* 22 */ DKIENT(sched_dpc),
97
        /* 23 */ DKIENT(task_capable),
98
        /* 24 */ DKIENT(exception_post),
99
        /* 25 */ DKIENT(device_create),
100
        /* 26 */ DKIENT(device_destroy),
101
        /* 27 */ DKIENT(device_lookup),
102
        /* 28 */ DKIENT(device_control),
103
        /* 29 */ DKIENT(device_broadcast),
104
        /* 30 */ DKIENT(device_private),
105
        /* 31 */ DKIENT(machine_bootinfo),
106
        /* 32 */ DKIENT(machine_powerdown),
107
        /* 33 */ DKIENT(sysinfo),
108
#ifdef DEBUG
109
        /* 34 */ DKIENT(panic),
110
        /* 35 */ DKIENT(printf),
111
        /* 36 */ DKIENT(dbgctl),
112
#else
113
        /* 34 */ DKIENT(machine_abort),
114
        /* 35 */ DKIENT(sys_nosys),
115
        /* 36 */ DKIENT(sys_nosys),
116
#endif
117
};
118
119
/* list head of the devices */
120
static struct device *device_list = NULL;
121
122
/*
123
 * device_create - create new device object.
124
 *
125
 * A device object is created by the device driver to provide
126
 * I/O services to applications.  Returns device ID on
127
 * success, or 0 on failure.
128
 */
129
static device_t
130
device_create(struct driver *drv, const char *name, int flags)
131
{
132
        device_t dev;
133
        size_t len;
134
        void *private = NULL;
135
136
        ASSERT(drv != NULL);
137
138
        /* Check the length of name. */
139
        len = strnlen(name, MAXDEVNAME);
140
        if (len == 0 || len >= MAXDEVNAME)
141
                return NULL;
142
143
        sched_lock();
144
145
        /* Check if specified name is already used. */
146
        if (device_lookup(name) != NULL)
147
                panic("duplicate device");
148
149
        /*
150
         * Allocate a device structure and device private data.
151
         */
152
        if ((dev = kmem_alloc(sizeof(*dev))) == NULL)
153
                panic("device_create");
154
155
        if (drv->devsz != 0) {
156
                if ((private = kmem_alloc(drv->devsz)) == NULL)
157
                        panic("devsz");
158
                memset(private, 0, drv->devsz);
159
        }
160
        strlcpy(dev->name, name, len + 1);
161
        dev->driver = drv;
162
        dev->flags = flags;
163
        dev->active = 1;
164
        dev->refcnt = 1;
165
        dev->private = private;
166
        dev->next = device_list;
167
        device_list = dev;
168
169
        sched_unlock();
170
        return dev;
171
}
172
173
/*
174
 * Destroy a device object. If some other threads still
175
 * refer the target device, the destroy operation will be
176
 * pending until its reference count becomes 0.
177
 */
178
static int
179
device_destroy(device_t dev)
180
{
181
182
        sched_lock();
183
        if (!device_valid(dev)) {
184
                sched_unlock();
185
                return ENODEV;
186
        }
187
        dev->active = 0;
188
        device_release(dev);
189
        sched_unlock();
190
        return 0;
191
}
192
193
/*
194
 * Look up a device object by device name.
195
 */
196
static device_t
197
device_lookup(const char *name)
198
{
199
        device_t dev;
200
201
        for (dev = device_list; dev != NULL; dev = dev->next) {
202
                if (!strncmp(dev->name, name, MAXDEVNAME))
203
                        return dev;
204
        }
205
        return NULL;
206
}
207
208
/*
209
 * Return device's private data.
210
 */
211
static void *
212
device_private(device_t dev)
213
{
214
        ASSERT(dev != NULL);
215
        ASSERT(dev->private != NULL);
216
217
        return dev->private;
218
}
219
220
/*
221
 * Return true if specified device is valid.
222
 */
223
static int
224
device_valid(device_t dev)
225
{
226
        device_t tmp;
227
        int found = 0;
228
229
        for (tmp = device_list; tmp != NULL; tmp = tmp->next) {
230
                if (tmp == dev) {
231
                        found = 1;
232
                        break;
233
                }
234
        }
235
        if (found && dev->active)
236
                return 1;
237
        return 0;
238
}
239
240
/*
241
 * Increment the reference count on an active device.
242
 */
243
static int
244
device_reference(device_t dev)
245
{
246
247
        sched_lock();
248
        if (!device_valid(dev)) {
249
                sched_unlock();
250
                return ENODEV;
251
        }
252
        if (!task_capable(CAP_RAWIO)) {
253
                sched_unlock();
254
                return EPERM;
255
        }
256
        dev->refcnt++;
257
        sched_unlock();
258
        return 0;
259
}
260
261
/*
262
 * Decrement the reference count on a device. If the reference
263
 * count becomes zero, we can release the resource for the device.
264
 */
265
static void
266
device_release(device_t dev)
267
{
268
        device_t *tmp;
269
270
        sched_lock();
271
        if (--dev->refcnt > 0) {
272
                sched_unlock();
273
                return;
274
        }
275
        /*
276
         * No more references - we can remove the device.
277
         */
278
        for (tmp = &device_list; *tmp; tmp = &(*tmp)->next) {
279
                if (*tmp == dev) {
280
                        *tmp = dev->next;
281
                        break;
282
                }
283
        }
284
        kmem_free(dev);
285
        sched_unlock();
286
}
287
288
/*
289
 * device_open - open the specified device.
290
 *
291
 * Even if the target driver does not have an open
292
 * routine, this function does not return an error. By
293
 * using this mechanism, an application can check whether
294
 * the specific device exists or not. The open mode
295
 * should be handled by an each device driver if it is
296
 * needed.
297
 */
298
int
299
device_open(const char *name, int mode, device_t *devp)
300
{
301
        struct devops *ops;
302
        device_t dev;
303
        char str[MAXDEVNAME];
304
        int error;
305
306
        error = copyinstr(name, str, MAXDEVNAME);
307
        if (error)
308
                return error;
309
310
        sched_lock();
311
        if ((dev = device_lookup(str)) == NULL) {
312
                sched_unlock();
313
                return ENXIO;
314
        }
315
        error = device_reference(dev);
316
        if (error) {
317
                sched_unlock();
318
                return error;
319
        }
320
        sched_unlock();
321
322
        ops = dev->driver->devops;
323
        ASSERT(ops->open != NULL);
324
        error = (*ops->open)(dev, mode);
325
        if (!error)
326
                error = copyout(&dev, devp, sizeof(dev));
327
328
        device_release(dev);
329
        return error;
330
}
331
332
/*
333
 * device_close - close a device.
334
 *
335
 * Even if the target driver does not have close routine,
336
 * this function does not return any errors.
337
 */
338
int
339
device_close(device_t dev)
340
{
341
        struct devops *ops;
342
        int error;
343
344
        if ((error = device_reference(dev)) != 0)
345
                return error;
346
347
        ops = dev->driver->devops;
348
        ASSERT(ops->close != NULL);
349
        error = (*ops->close)(dev);
350
351
        device_release(dev);
352
        return error;
353
}
354
355
/*
356
 * device_read - read from a device.
357
 *
358
 * Actual read count is set in "nbyte" as return.
359
 * Note: The size of one block is device dependent.
360
 */
361
int
362
device_read(device_t dev, void *buf, size_t *nbyte, int blkno)
363
{
364
        struct devops *ops;
365
        size_t count;
366
        int error;
367
368
        if (!user_area(buf))
369
                return EFAULT;
370
371
        if ((error = device_reference(dev)) != 0)
372
                return error;
373
374
        if (copyin(nbyte, &count, sizeof(count))) {
375
                device_release(dev);
376
                return EFAULT;
377
        }
378
379
        ops = dev->driver->devops;
380
        ASSERT(ops->read != NULL);
381
        error = (*ops->read)(dev, buf, &count, blkno);
382
        if (!error)
383
                error = copyout(&count, nbyte, sizeof(count));
384
385
        device_release(dev);
386
        return error;
387
}
388
389
/*
390
 * device_write - write to a device.
391
 *
392
 * Actual write count is set in "nbyte" as return.
393
 */
394
int
395
device_write(device_t dev, void *buf, size_t *nbyte, int blkno)
396
{
397
        struct devops *ops;
398
        size_t count;
399
        int error;
400
401
        if (!user_area(buf))
402
                return EFAULT;
403
404
        if ((error = device_reference(dev)) != 0)
405
                return error;
406
407
        if (copyin(nbyte, &count, sizeof(count))) {
408
                device_release(dev);
409
                return EFAULT;
410
        }
411
412
        ops = dev->driver->devops;
413
        ASSERT(ops->write != NULL);
414
        error = (*ops->write)(dev, buf, &count, blkno);
415
        if (!error)
416
                error = copyout(&count, nbyte, sizeof(count));
417
418
        device_release(dev);
419
        return error;
420
}
421
422
/*
423
 * device_ioctl - I/O control request.
424
 *
425
 * A command and its argument are completely device dependent.
426
 * The ioctl routine of each driver must validate the user buffer
427
 * pointed by the arg value.
428
 */
429
int
430
device_ioctl(device_t dev, u_long cmd, void *arg)
431
{
432
        struct devops *ops;
433
        int error;
434
435
        if ((error = device_reference(dev)) != 0)
436
                return error;
437
438
        ops = dev->driver->devops;
439
        ASSERT(ops->ioctl != NULL);
440
        error = (*ops->ioctl)(dev, cmd, arg);
441
442
        device_release(dev);
443
        return error;
444
}
445
446
/*
447
 * Device control - devctl is similar to ioctl, but is invoked from
448
 * other device driver rather than from user application.
449
 */
450
static int
451
device_control(device_t dev, u_long cmd, void *arg)
452
{
453
        struct devops *ops;
454
        int error;
455
456
        ASSERT(dev != NULL);
457
458
        sched_lock();
459
        ops = dev->driver->devops;
460
        ASSERT(ops->devctl != NULL);
461
        error = (*ops->devctl)(dev, cmd, arg);
462
        sched_unlock();
463
        return error;
464
}
465
466
/*
467
 * device_broadcast - broadcast devctl command to all device objects.
468
 *
469
 * If "force" argument is true, we will continue command
470
 * notification even if some driver returns an error. In this
471
 * case, this routine returns EIO error if at least one driver
472
 * returns an error.
473
 *
474
 * If force argument is false, a kernel stops the command processing
475
 * when at least one driver returns an error. In this case,
476
 * device_broadcast will return the error code which is returned
477
 * by the driver.
478
 */
479
static int
480
device_broadcast(u_long cmd, void *arg, int force)
481
{
482
        device_t dev;
483
        struct devops *ops;
484
        int error, retval = 0;
485
486
        sched_lock();
487
488
        for (dev = device_list; dev != NULL; dev = dev->next) {
489
                /*
490
                 * Call driver's devctl() routine.
491
                 */
492
                ops = dev->driver->devops;
493
                if (ops == NULL)
494
                        continue;
495
496
                ASSERT(ops->devctl != NULL);
497
                error = (*ops->devctl)(dev, cmd, arg);
498
                if (error) {
499
                        DPRINTF(("%s returns error=%d for cmd=%ld\n",
500
                                 dev->name, error, cmd));
501
                        if (force)
502
                                retval = EIO;
503
                        else {
504
                                retval = error;
505
                                break;
506
                        }
507
                }
508
        }
509
        sched_unlock();
510
        return retval;
511
}
512
513
/*
514
 * Return device information.
515
 */
516
int
517
device_info(struct devinfo *info)
518
{
519
        u_long target = info->cookie;
520
        u_long i = 0;
521
        device_t dev;
522
        int error = ESRCH;
523
524
        sched_lock();
525
        for (dev = device_list; dev != NULL; dev = dev->next) {
526
                if (i++ == target) {
527
                        info->cookie = i;
528
                        info->id = dev;
529
                        info->flags = dev->flags;
530
                        strlcpy(info->name, dev->name, MAXDEVNAME);
531
                        error = 0;
532
                        break;
533
                }
534
        }
535
        sched_unlock();
536
        return error;
537
}
538
539
/*
540
 * Initialize device driver module.
541
 */
542
void
543
device_init(void)
544
{
545
        struct module *mod;
546
        struct bootinfo *bi;
547
        void (*entry)(const dkifn_t *);
548
549
        machine_bootinfo(&bi);
550
        mod = &bi->driver;
551
        if (mod == NULL) {
552
                DPRINTF(("Warning: No driver found\n"));
553
                return;
554
        }
555
        entry = (void (*)(const dkifn_t *))mod->entry;
556
        ASSERT(entry != NULL);
557
558
        /* Show module location to add the driver symbols for gdb. */
559
        DPRINTF(("Entering driver module (at 0x%x)\n", (int)entry));
560
561
        (*entry)(dkient);
562
}