2005-05-29 02:51:58 +04:00
/* 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 .
*/
2005-04-17 02:20:36 +04:00
# 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 */
2006-09-29 12:58:50 +04:00
/* 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 .
*/
2005-04-17 02:20:36 +04:00
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 ;
2005-11-07 12:01:14 +03:00
if ( need_resched ( ) )
schedule_timeout_interruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
else return n ;
if ( signal_pending ( current ) )
return ret ? : - ERESTARTSYS ;
}
return ret ;
}
2006-09-27 12:50:33 +04:00
static const struct file_operations rng_chrdev_ops = {
2005-04-17 02:20:36 +04:00
. 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 ) {
2005-05-29 02:51:58 +04:00
printk ( KERN_ERR RNG_MODULE_NAME " : misc device register failed \n " ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2005-05-29 02:51:58 +04:00
MODULE_DESCRIPTION ( " UML Host Random Number Generator (RNG) driver " ) ;
MODULE_LICENSE ( " GPL " ) ;