|
|||
Prex Home / Browse Source - Prex Version: 0.9.0 |
|||
root/sys/kern/device.c/* [<][>][^][v][top][bottom][index][help] */DEFINITIONSThis source file includes following definitions.
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 } /* [<][>][^][v][top][bottom][index][help] */ | |||
Copyright© 2005-2009 Kohsuke Ohtani |