root / prex-0.9.0 / sys / kern / device.c @ 03e9c04a
History | View | Annotate | Download (12.2 KB)
1 |
/*-
|
---|---|
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 |
} |