| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |         Added support for the AMD Geode LX RNG | 
					
						
							|  |  |  | 	(c) Copyright 2004-2005 Advanced Micro Devices, Inc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	derived from | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  	Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) | 
					
						
							|  |  |  | 	(c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  	derived from | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Hardware driver for the AMD 768 Random Number Generator (RNG) | 
					
						
							|  |  |  |         (c) Copyright 2001 Red Hat Inc <alan@redhat.com> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  	derived from | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Hardware driver for Intel i810 Random Number Generator (RNG) | 
					
						
							|  |  |  | 	Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> | 
					
						
							|  |  |  | 	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Added generic RNG API | 
					
						
							|  |  |  | 	Copyright 2006 Michael Buesch <mbuesch@freenet.de> | 
					
						
							|  |  |  | 	Copyright 2005 (c) MontaVista Software, Inc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Please read Documentation/hw_random.txt for details on use. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	---------------------------------------------------------- | 
					
						
							|  |  |  | 	This software may be used and distributed according to the terms | 
					
						
							|  |  |  |         of the GNU General Public License, incorporated herein by reference. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/hw_random.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/fs.h>
 | 
					
						
							| 
									
										
										
										
											2006-10-18 13:55:46 -04:00
										 |  |  | #include <linux/sched.h>
 | 
					
						
							| 
									
										
										
										
											2008-05-20 19:16:00 +02:00
										 |  |  | #include <linux/smp_lock.h>
 | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/miscdevice.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define RNG_MODULE_NAME		"hw_random"
 | 
					
						
							|  |  |  | #define PFX			RNG_MODULE_NAME ": "
 | 
					
						
							|  |  |  | #define RNG_MISCDEV_MINOR	183 /* official */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct hwrng *current_rng; | 
					
						
							|  |  |  | static LIST_HEAD(rng_list); | 
					
						
							|  |  |  | static DEFINE_MUTEX(rng_mutex); | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | static int data_avail; | 
					
						
							| 
									
										
										
										
											2009-12-03 13:50:42 +08:00
										 |  |  | static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES] | 
					
						
							|  |  |  | 	__cacheline_aligned; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static inline int hwrng_init(struct hwrng *rng) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!rng->init) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	return rng->init(rng); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void hwrng_cleanup(struct hwrng *rng) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (rng && rng->cleanup) | 
					
						
							|  |  |  | 		rng->cleanup(rng); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, | 
					
						
							|  |  |  | 			int wait) { | 
					
						
							|  |  |  | 	int present; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rng->read) | 
					
						
							|  |  |  | 		return rng->read(rng, (void *)buffer, size, wait); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rng->data_present) | 
					
						
							|  |  |  | 		present = rng->data_present(rng, wait); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		present = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (present) | 
					
						
							|  |  |  | 		return rng->data_read(rng, (u32 *)buffer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | static ssize_t rng_dev_read(struct file *filp, char __user *buf, | 
					
						
							|  |  |  | 			    size_t size, loff_t *offp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t ret = 0; | 
					
						
							| 
									
										
										
										
											2007-11-21 12:24:45 +08:00
										 |  |  | 	int err = 0; | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 	int bytes_read, len; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	while (size) { | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		if (mutex_lock_interruptible(&rng_mutex)) { | 
					
						
							|  |  |  | 			err = -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 		if (!current_rng) { | 
					
						
							|  |  |  | 			err = -ENODEV; | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 			goto out_unlock; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-11-21 12:24:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		if (!data_avail) { | 
					
						
							|  |  |  | 			bytes_read = rng_get_data(current_rng, rng_buffer, | 
					
						
							|  |  |  | 				sizeof(rng_buffer), | 
					
						
							|  |  |  | 				!(filp->f_flags & O_NONBLOCK)); | 
					
						
							|  |  |  | 			if (bytes_read < 0) { | 
					
						
							|  |  |  | 				err = bytes_read; | 
					
						
							|  |  |  | 				goto out_unlock; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			data_avail = bytes_read; | 
					
						
							| 
									
										
										
										
											2008-04-17 07:46:14 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		if (!data_avail) { | 
					
						
							|  |  |  | 			if (filp->f_flags & O_NONBLOCK) { | 
					
						
							|  |  |  | 				err = -EAGAIN; | 
					
						
							|  |  |  | 				goto out_unlock; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			len = data_avail; | 
					
						
							|  |  |  | 			if (len > size) | 
					
						
							|  |  |  | 				len = size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			data_avail -= len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (copy_to_user(buf + ret, rng_buffer + data_avail, | 
					
						
							|  |  |  | 								len)) { | 
					
						
							|  |  |  | 				err = -EFAULT; | 
					
						
							|  |  |  | 				goto out_unlock; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			size -= len; | 
					
						
							|  |  |  | 			ret += len; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 		if (need_resched()) | 
					
						
							|  |  |  | 			schedule_timeout_interruptible(1); | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (signal_pending(current)) { | 
					
						
							|  |  |  | 			err = -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret ? : err; | 
					
						
							| 
									
										
										
										
											2009-12-23 23:22:34 +08:00
										 |  |  | out_unlock: | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | 	goto out; | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-03 00:24:21 -07:00
										 |  |  | static const struct file_operations rng_chrdev_ops = { | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 	.owner		= THIS_MODULE, | 
					
						
							|  |  |  | 	.open		= rng_dev_open, | 
					
						
							|  |  |  | 	.read		= rng_dev_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct miscdevice rng_miscdev = { | 
					
						
							|  |  |  | 	.minor		= RNG_MISCDEV_MINOR, | 
					
						
							|  |  |  | 	.name		= RNG_MODULE_NAME, | 
					
						
							| 
									
										
										
										
											2009-09-18 23:01:12 +02:00
										 |  |  | 	.nodename	= "hwrng", | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 	.fops		= &rng_chrdev_ops, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | static ssize_t hwrng_attr_current_store(struct device *dev, | 
					
						
							|  |  |  | 					struct device_attribute *attr, | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 					const char *buf, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	struct hwrng *rng; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mutex_lock_interruptible(&rng_mutex); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	err = -ENODEV; | 
					
						
							|  |  |  | 	list_for_each_entry(rng, &rng_list, list) { | 
					
						
							|  |  |  | 		if (strcmp(rng->name, buf) == 0) { | 
					
						
							|  |  |  | 			if (rng == current_rng) { | 
					
						
							|  |  |  | 				err = 0; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			err = hwrng_init(rng); | 
					
						
							|  |  |  | 			if (err) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			hwrng_cleanup(current_rng); | 
					
						
							|  |  |  | 			current_rng = rng; | 
					
						
							|  |  |  | 			err = 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err ? : len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | static ssize_t hwrng_attr_current_show(struct device *dev, | 
					
						
							|  |  |  | 				       struct device_attribute *attr, | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 				       char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	ssize_t ret; | 
					
						
							|  |  |  | 	const char *name = "none"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mutex_lock_interruptible(&rng_mutex); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	if (current_rng) | 
					
						
							|  |  |  | 		name = current_rng->name; | 
					
						
							|  |  |  | 	ret = snprintf(buf, PAGE_SIZE, "%s\n", name); | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | static ssize_t hwrng_attr_available_show(struct device *dev, | 
					
						
							|  |  |  | 					 struct device_attribute *attr, | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 					 char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	ssize_t ret = 0; | 
					
						
							|  |  |  | 	struct hwrng *rng; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mutex_lock_interruptible(&rng_mutex); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	buf[0] = '\0'; | 
					
						
							|  |  |  | 	list_for_each_entry(rng, &rng_list, list) { | 
					
						
							|  |  |  | 		strncat(buf, rng->name, PAGE_SIZE - ret - 1); | 
					
						
							|  |  |  | 		ret += strlen(rng->name); | 
					
						
							|  |  |  | 		strncat(buf, " ", PAGE_SIZE - ret - 1); | 
					
						
							|  |  |  | 		ret++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	strncat(buf, "\n", PAGE_SIZE - ret - 1); | 
					
						
							|  |  |  | 	ret++; | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR, | 
					
						
							|  |  |  | 		   hwrng_attr_current_show, | 
					
						
							|  |  |  | 		   hwrng_attr_current_store); | 
					
						
							|  |  |  | static DEVICE_ATTR(rng_available, S_IRUGO, | 
					
						
							|  |  |  | 		   hwrng_attr_available_show, | 
					
						
							|  |  |  | 		   NULL); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-23 20:28:24 +01:00
										 |  |  | static void unregister_miscdev(void) | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available); | 
					
						
							|  |  |  | 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); | 
					
						
							| 
									
										
										
										
											2008-03-23 20:28:24 +01:00
										 |  |  | 	misc_deregister(&rng_miscdev); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int register_miscdev(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = misc_register(&rng_miscdev); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | 	err = device_create_file(rng_miscdev.this_device, | 
					
						
							|  |  |  | 				 &dev_attr_rng_current); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto err_misc_dereg; | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | 	err = device_create_file(rng_miscdev.this_device, | 
					
						
							|  |  |  | 				 &dev_attr_rng_available); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto err_remove_current; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err_remove_current: | 
					
						
							| 
									
										
										
										
											2006-07-27 16:16:04 -07:00
										 |  |  | 	device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | err_misc_dereg: | 
					
						
							|  |  |  | 	misc_deregister(&rng_miscdev); | 
					
						
							|  |  |  | 	goto out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int hwrng_register(struct hwrng *rng) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int must_register_misc; | 
					
						
							|  |  |  | 	int err = -EINVAL; | 
					
						
							|  |  |  | 	struct hwrng *old_rng, *tmp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rng->name == NULL || | 
					
						
							| 
									
										
										
										
											2009-12-01 14:47:32 +08:00
										 |  |  | 	    (rng->data_read == NULL && rng->read == NULL)) | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Must not register two RNGs with the same name. */ | 
					
						
							|  |  |  | 	err = -EEXIST; | 
					
						
							|  |  |  | 	list_for_each_entry(tmp, &rng_list, list) { | 
					
						
							|  |  |  | 		if (strcmp(tmp->name, rng->name) == 0) | 
					
						
							|  |  |  | 			goto out_unlock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	must_register_misc = (current_rng == NULL); | 
					
						
							|  |  |  | 	old_rng = current_rng; | 
					
						
							|  |  |  | 	if (!old_rng) { | 
					
						
							|  |  |  | 		err = hwrng_init(rng); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto out_unlock; | 
					
						
							|  |  |  | 		current_rng = rng; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | 	if (must_register_misc) { | 
					
						
							|  |  |  | 		err = register_miscdev(); | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			if (!old_rng) { | 
					
						
							|  |  |  | 				hwrng_cleanup(rng); | 
					
						
							|  |  |  | 				current_rng = NULL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			goto out_unlock; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	INIT_LIST_HEAD(&rng->list); | 
					
						
							|  |  |  | 	list_add_tail(&rng->list, &rng_list); | 
					
						
							|  |  |  | out_unlock: | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(hwrng_register); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-23 20:28:24 +01:00
										 |  |  | void hwrng_unregister(struct hwrng *rng) | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&rng_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_del(&rng->list); | 
					
						
							|  |  |  | 	if (current_rng == rng) { | 
					
						
							|  |  |  | 		hwrng_cleanup(rng); | 
					
						
							|  |  |  | 		if (list_empty(&rng_list)) { | 
					
						
							|  |  |  | 			current_rng = NULL; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			current_rng = list_entry(rng_list.prev, struct hwrng, list); | 
					
						
							|  |  |  | 			err = hwrng_init(current_rng); | 
					
						
							|  |  |  | 			if (err) | 
					
						
							|  |  |  | 				current_rng = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (list_empty(&rng_list)) | 
					
						
							| 
									
										
										
										
											2008-03-23 20:28:24 +01:00
										 |  |  | 		unregister_miscdev(); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&rng_mutex); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2008-03-23 20:28:24 +01:00
										 |  |  | EXPORT_SYMBOL_GPL(hwrng_unregister); | 
					
						
							| 
									
										
										
										
											2006-06-26 00:24:59 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |