diff options
| author | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 | 
|---|---|---|
| committer | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 | 
| commit | a8931ef380c92d121ae74ecfb03b2d63f72eea6f (patch) | |
| tree | 980fb6b019e11e6cb1ece55b7faff184721a8053 /arch/um/drivers/random.c | |
| parent | 90574d0a4d4b73308ae54a2a57a4f3f1fa98e984 (diff) | |
| parent | e5a5816f7875207cb0a0a7032e39a4686c5e10a4 (diff) | |
| download | olio-linux-3.10-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.tar.xz olio-linux-3.10-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.zip  | |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'arch/um/drivers/random.c')
| -rw-r--r-- | arch/um/drivers/random.c | 118 | 
1 files changed, 77 insertions, 41 deletions
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c index 71f0959c153..4949044773b 100644 --- a/arch/um/drivers/random.c +++ b/arch/um/drivers/random.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2005 Jeff Dike <jdike@addtoit.com> */ +/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */ +  /* Much of this ripped from drivers/char/hw_random.c, see there for other   * copyright.   * @@ -8,16 +9,18 @@  #include <linux/sched.h>  #include <linux/module.h>  #include <linux/fs.h> +#include <linux/interrupt.h>  #include <linux/miscdevice.h>  #include <linux/delay.h>  #include <asm/uaccess.h> +#include "irq_kern.h"  #include "os.h"  /*   * core module and version information   */  #define RNG_VERSION "1.0.0" -#define RNG_MODULE_NAME "random" +#define RNG_MODULE_NAME "hw_random"  #define RNG_MISCDEV_MINOR		183 /* official */ @@ -26,47 +29,67 @@   * protects against a module being loaded twice at the same time.   */  static int random_fd = -1; +static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);  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) +	if ((filp->f_mode & FMODE_WRITE) != 0)  		return -EINVAL;  	return 0;  } +static atomic_t host_sleep_count = ATOMIC_INIT(0); +  static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, -                             loff_t * offp) +			     loff_t *offp)  { -        u32 data; -        int n, ret = 0, have_data; +	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) { +			DECLARE_WAITQUEUE(wait, current); + +			if (filp->f_flags & O_NONBLOCK) +				return ret ? : -EAGAIN; + +			atomic_inc(&host_sleep_count); +			reactivate_fd(random_fd, RANDOM_IRQ); +			add_sigio_fd(random_fd); + +			add_wait_queue(&host_read_wait, &wait); +			set_task_state(current, TASK_INTERRUPTIBLE); + +			schedule(); +			set_task_state(current, TASK_RUNNING); +			remove_wait_queue(&host_read_wait, &wait); -        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; +			if (atomic_dec_and_test(&host_sleep_count)) { +				ignore_sigio_fd(random_fd); +				deactivate_fd(random_fd, RANDOM_IRQ); +			} +		} +		else +			return n; -                        if(need_resched()) -                                schedule_timeout_interruptible(1); -                } -                else return n;  		if (signal_pending (current))  			return ret ? : -ERESTARTSYS;  	} @@ -86,6 +109,13 @@ static struct miscdevice rng_miscdev = {  	&rng_chrdev_ops,  }; +static irqreturn_t random_interrupt(int irq, void *data) +{ +	wake_up(&host_read_wait); + +	return IRQ_HANDLED; +} +  /*   * rng_init - initialize RNG module   */ @@ -93,28 +123,33 @@ static int __init rng_init (void)  {  	int err; -        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); -        if(err < 0) -                goto out; +	err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); +	if (err < 0) +		goto out; -        random_fd = err; +	random_fd = err; -        err = os_set_fd_block(random_fd, 0); -        if(err) +	err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, +			     IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random", +			     NULL); +	if (err)  		goto err_out_cleanup_hw; +	sigio_broken(random_fd, 1); +  	err = misc_register (&rng_miscdev);  	if (err) { -		printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n"); +		printk (KERN_ERR RNG_MODULE_NAME ": misc device register " +			"failed\n");  		goto err_out_cleanup_hw;  	} +out: +	return err; - out: -        return err; - - err_out_cleanup_hw: -        random_fd = -1; -        goto out; +err_out_cleanup_hw: +	os_close_file(random_fd); +	random_fd = -1; +	goto out;  }  /* @@ -122,6 +157,7 @@ static int __init rng_init (void)   */  static void __exit rng_cleanup (void)  { +	os_close_file(random_fd);  	misc_deregister (&rng_miscdev);  }  |