random.c 3.2 KB
Newer Older
1 2 3 4 5 6 7
/* Copyright (C) 2005 Jeff Dike <jdike@addtoit.com> */
/* Much of this ripped from drivers/char/hw_random.c, see there for other
 * copyright.
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 */
L
Linus Torvalds 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include "os.h"

/*
 * core module and version information
 */
#define RNG_VERSION "1.0.0"
#define RNG_MODULE_NAME "random"

#define RNG_MISCDEV_MINOR		183 /* official */

J
Jeff Dike 已提交
23 24 25 26
/* Changed at init time, in the non-modular case, and at module load
 * time, in the module case.  Presumably, the module subsystem
 * protects against a module being loaded twice at the same time.
 */
L
Linus Torvalds 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
static int random_fd = -1;

static int rng_dev_open (struct inode *inode, struct file *filp)
{
	/* enforce read-only access to this chrdev */
	if ((filp->f_mode & FMODE_READ) == 0)
		return -EINVAL;
	if (filp->f_mode & FMODE_WRITE)
		return -EINVAL;

	return 0;
}

static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
                             loff_t * offp)
{
        u32 data;
        int n, ret = 0, have_data;

        while(size){
                n = os_read_file(random_fd, &data, sizeof(data));
                if(n > 0){
                        have_data = n;
                        while (have_data && size) {
                                if (put_user((u8)data, buf++)) {
                                        ret = ret ? : -EFAULT;
                                        break;
                                }
                                size--;
                                ret++;
                                have_data--;
                                data>>=8;
                        }
                }
                else if(n == -EAGAIN){
                        if (filp->f_flags & O_NONBLOCK)
                                return ret ? : -EAGAIN;

65 66
                        if(need_resched())
                                schedule_timeout_interruptible(1);
L
Linus Torvalds 已提交
67 68 69 70 71 72 73 74
                }
                else return n;
		if (signal_pending (current))
			return ret ? : -ERESTARTSYS;
	}
	return ret;
}

J
Jeff Dike 已提交
75
static const struct file_operations rng_chrdev_ops = {
L
Linus Torvalds 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
	.owner		= THIS_MODULE,
	.open		= rng_dev_open,
	.read		= rng_dev_read,
};

static struct miscdevice rng_miscdev = {
	RNG_MISCDEV_MINOR,
	RNG_MODULE_NAME,
	&rng_chrdev_ops,
};

/*
 * rng_init - initialize RNG module
 */
static int __init rng_init (void)
{
	int err;

        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
        if(err < 0)
                goto out;

        random_fd = err;

        err = os_set_fd_block(random_fd, 0);
        if(err)
		goto err_out_cleanup_hw;

	err = misc_register (&rng_miscdev);
	if (err) {
106
		printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n");
L
Linus Torvalds 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
		goto err_out_cleanup_hw;
	}

 out:
        return err;

 err_out_cleanup_hw:
        random_fd = -1;
        goto out;
}

/*
 * rng_cleanup - shutdown RNG module
 */
static void __exit rng_cleanup (void)
{
	misc_deregister (&rng_miscdev);
}

module_init (rng_init);
module_exit (rng_cleanup);
128 129 130

MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");