linux/arch/um/drivers/random.c
<<
>>
Prefs
   1/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
   2
   3/* Much of this ripped from drivers/char/hw_random.c, see there for other
   4 * copyright.
   5 *
   6 * This software may be used and distributed according to the terms
   7 * of the GNU General Public License, incorporated herein by reference.
   8 */
   9#include <linux/sched.h>
  10#include <linux/module.h>
  11#include <linux/fs.h>
  12#include <linux/interrupt.h>
  13#include <linux/miscdevice.h>
  14#include <linux/delay.h>
  15#include <asm/uaccess.h>
  16#include <irq_kern.h>
  17#include <os.h>
  18
  19/*
  20 * core module and version information
  21 */
  22#define RNG_VERSION "1.0.0"
  23#define RNG_MODULE_NAME "hw_random"
  24
  25#define RNG_MISCDEV_MINOR               183 /* official */
  26
  27/* Changed at init time, in the non-modular case, and at module load
  28 * time, in the module case.  Presumably, the module subsystem
  29 * protects against a module being loaded twice at the same time.
  30 */
  31static int random_fd = -1;
  32static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
  33
  34static int rng_dev_open (struct inode *inode, struct file *filp)
  35{
  36        /* enforce read-only access to this chrdev */
  37        if ((filp->f_mode & FMODE_READ) == 0)
  38                return -EINVAL;
  39        if ((filp->f_mode & FMODE_WRITE) != 0)
  40                return -EINVAL;
  41
  42        return 0;
  43}
  44
  45static atomic_t host_sleep_count = ATOMIC_INIT(0);
  46
  47static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
  48                             loff_t *offp)
  49{
  50        u32 data;
  51        int n, ret = 0, have_data;
  52
  53        while (size) {
  54                n = os_read_file(random_fd, &data, sizeof(data));
  55                if (n > 0) {
  56                        have_data = n;
  57                        while (have_data && size) {
  58                                if (put_user((u8) data, buf++)) {
  59                                        ret = ret ? : -EFAULT;
  60                                        break;
  61                                }
  62                                size--;
  63                                ret++;
  64                                have_data--;
  65                                data >>= 8;
  66                        }
  67                }
  68                else if (n == -EAGAIN) {
  69                        DECLARE_WAITQUEUE(wait, current);
  70
  71                        if (filp->f_flags & O_NONBLOCK)
  72                                return ret ? : -EAGAIN;
  73
  74                        atomic_inc(&host_sleep_count);
  75                        reactivate_fd(random_fd, RANDOM_IRQ);
  76                        add_sigio_fd(random_fd);
  77
  78                        add_wait_queue(&host_read_wait, &wait);
  79                        set_task_state(current, TASK_INTERRUPTIBLE);
  80
  81                        schedule();
  82                        set_task_state(current, TASK_RUNNING);
  83                        remove_wait_queue(&host_read_wait, &wait);
  84
  85                        if (atomic_dec_and_test(&host_sleep_count)) {
  86                                ignore_sigio_fd(random_fd);
  87                                deactivate_fd(random_fd, RANDOM_IRQ);
  88                        }
  89                }
  90                else
  91                        return n;
  92
  93                if (signal_pending (current))
  94                        return ret ? : -ERESTARTSYS;
  95        }
  96        return ret;
  97}
  98
  99static const struct file_operations rng_chrdev_ops = {
 100        .owner          = THIS_MODULE,
 101        .open           = rng_dev_open,
 102        .read           = rng_dev_read,
 103        .llseek         = noop_llseek,
 104};
 105
 106/* rng_init shouldn't be called more than once at boot time */
 107static struct miscdevice rng_miscdev = {
 108        RNG_MISCDEV_MINOR,
 109        RNG_MODULE_NAME,
 110        &rng_chrdev_ops,
 111};
 112
 113static irqreturn_t random_interrupt(int irq, void *data)
 114{
 115        wake_up(&host_read_wait);
 116
 117        return IRQ_HANDLED;
 118}
 119
 120/*
 121 * rng_init - initialize RNG module
 122 */
 123static int __init rng_init (void)
 124{
 125        int err;
 126
 127        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
 128        if (err < 0)
 129                goto out;
 130
 131        random_fd = err;
 132
 133        err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
 134                             0, "random", NULL);
 135        if (err)
 136                goto err_out_cleanup_hw;
 137
 138        sigio_broken(random_fd, 1);
 139
 140        err = misc_register (&rng_miscdev);
 141        if (err) {
 142                printk (KERN_ERR RNG_MODULE_NAME ": misc device register "
 143                        "failed\n");
 144                goto err_out_cleanup_hw;
 145        }
 146out:
 147        return err;
 148
 149err_out_cleanup_hw:
 150        os_close_file(random_fd);
 151        random_fd = -1;
 152        goto out;
 153}
 154
 155/*
 156 * rng_cleanup - shutdown RNG module
 157 */
 158static void __exit rng_cleanup (void)
 159{
 160        os_close_file(random_fd);
 161        misc_deregister (&rng_miscdev);
 162}
 163
 164module_init (rng_init);
 165module_exit (rng_cleanup);
 166
 167MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
 168MODULE_LICENSE("GPL");
 169