Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/kern/device.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. device_create
  2. device_destroy
  3. device_lookup
  4. device_private
  5. device_valid
  6. device_reference
  7. device_release
  8. device_open
  9. device_close
  10. device_read
  11. device_write
  12. device_ioctl
  13. device_control
  14. device_broadcast
  15. device_info
  16. device_init

   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] */