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