Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[linux-2.6] / drivers / char / hw_random / core.c
1 /*
2         Added support for the AMD Geode LX RNG
3         (c) Copyright 2004-2005 Advanced Micro Devices, Inc.
4
5         derived from
6
7         Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
8         (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
9
10         derived from
11
12         Hardware driver for the AMD 768 Random Number Generator (RNG)
13         (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
14
15         derived from
16
17         Hardware driver for Intel i810 Random Number Generator (RNG)
18         Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
19         Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
20
21         Added generic RNG API
22         Copyright 2006 Michael Buesch <mbuesch@freenet.de>
23         Copyright 2005 (c) MontaVista Software, Inc.
24
25         Please read Documentation/hw_random.txt for details on use.
26
27         ----------------------------------------------------------
28         This software may be used and distributed according to the terms
29         of the GNU General Public License, incorporated herein by reference.
30
31  */
32
33
34 #include <linux/device.h>
35 #include <linux/hw_random.h>
36 #include <linux/module.h>
37 #include <linux/kernel.h>
38 #include <linux/fs.h>
39 #include <linux/sched.h>
40 #include <linux/init.h>
41 #include <linux/miscdevice.h>
42 #include <linux/delay.h>
43 #include <asm/uaccess.h>
44
45
46 #define RNG_MODULE_NAME         "hw_random"
47 #define PFX                     RNG_MODULE_NAME ": "
48 #define RNG_MISCDEV_MINOR       183 /* official */
49
50
51 static struct hwrng *current_rng;
52 static LIST_HEAD(rng_list);
53 static DEFINE_MUTEX(rng_mutex);
54
55
56 static inline int hwrng_init(struct hwrng *rng)
57 {
58         if (!rng->init)
59                 return 0;
60         return rng->init(rng);
61 }
62
63 static inline void hwrng_cleanup(struct hwrng *rng)
64 {
65         if (rng && rng->cleanup)
66                 rng->cleanup(rng);
67 }
68
69 static inline int hwrng_data_present(struct hwrng *rng)
70 {
71         if (!rng->data_present)
72                 return 1;
73         return rng->data_present(rng);
74 }
75
76 static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
77 {
78         return rng->data_read(rng, data);
79 }
80
81
82 static int rng_dev_open(struct inode *inode, struct file *filp)
83 {
84         /* enforce read-only access to this chrdev */
85         if ((filp->f_mode & FMODE_READ) == 0)
86                 return -EINVAL;
87         if (filp->f_mode & FMODE_WRITE)
88                 return -EINVAL;
89         return 0;
90 }
91
92 static ssize_t rng_dev_read(struct file *filp, char __user *buf,
93                             size_t size, loff_t *offp)
94 {
95         u32 data;
96         ssize_t ret = 0;
97         int i, err = 0;
98         int data_present;
99         int bytes_read;
100
101         while (size) {
102                 err = -ERESTARTSYS;
103                 if (mutex_lock_interruptible(&rng_mutex))
104                         goto out;
105                 if (!current_rng) {
106                         mutex_unlock(&rng_mutex);
107                         err = -ENODEV;
108                         goto out;
109                 }
110                 if (filp->f_flags & O_NONBLOCK) {
111                         data_present = hwrng_data_present(current_rng);
112                 } else {
113                         /* Some RNG require some time between data_reads to gather
114                          * new entropy. Poll it.
115                          */
116                         for (i = 0; i < 20; i++) {
117                                 data_present = hwrng_data_present(current_rng);
118                                 if (data_present)
119                                         break;
120                                 udelay(10);
121                         }
122                 }
123                 bytes_read = 0;
124                 if (data_present)
125                         bytes_read = hwrng_data_read(current_rng, &data);
126                 mutex_unlock(&rng_mutex);
127
128                 err = -EAGAIN;
129                 if (!bytes_read && (filp->f_flags & O_NONBLOCK))
130                         goto out;
131
132                 err = -EFAULT;
133                 while (bytes_read && size) {
134                         if (put_user((u8)data, buf++))
135                                 goto out;
136                         size--;
137                         ret++;
138                         bytes_read--;
139                         data >>= 8;
140                 }
141
142                 if (need_resched())
143                         schedule_timeout_interruptible(1);
144                 err = -ERESTARTSYS;
145                 if (signal_pending(current))
146                         goto out;
147         }
148 out:
149         return ret ? : err;
150 }
151
152
153 static const struct file_operations rng_chrdev_ops = {
154         .owner          = THIS_MODULE,
155         .open           = rng_dev_open,
156         .read           = rng_dev_read,
157 };
158
159 static struct miscdevice rng_miscdev = {
160         .minor          = RNG_MISCDEV_MINOR,
161         .name           = RNG_MODULE_NAME,
162         .fops           = &rng_chrdev_ops,
163 };
164
165
166 static ssize_t hwrng_attr_current_store(struct device *dev,
167                                         struct device_attribute *attr,
168                                         const char *buf, size_t len)
169 {
170         int err;
171         struct hwrng *rng;
172
173         err = mutex_lock_interruptible(&rng_mutex);
174         if (err)
175                 return -ERESTARTSYS;
176         err = -ENODEV;
177         list_for_each_entry(rng, &rng_list, list) {
178                 if (strcmp(rng->name, buf) == 0) {
179                         if (rng == current_rng) {
180                                 err = 0;
181                                 break;
182                         }
183                         err = hwrng_init(rng);
184                         if (err)
185                                 break;
186                         hwrng_cleanup(current_rng);
187                         current_rng = rng;
188                         err = 0;
189                         break;
190                 }
191         }
192         mutex_unlock(&rng_mutex);
193
194         return err ? : len;
195 }
196
197 static ssize_t hwrng_attr_current_show(struct device *dev,
198                                        struct device_attribute *attr,
199                                        char *buf)
200 {
201         int err;
202         ssize_t ret;
203         const char *name = "none";
204
205         err = mutex_lock_interruptible(&rng_mutex);
206         if (err)
207                 return -ERESTARTSYS;
208         if (current_rng)
209                 name = current_rng->name;
210         ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
211         mutex_unlock(&rng_mutex);
212
213         return ret;
214 }
215
216 static ssize_t hwrng_attr_available_show(struct device *dev,
217                                          struct device_attribute *attr,
218                                          char *buf)
219 {
220         int err;
221         ssize_t ret = 0;
222         struct hwrng *rng;
223
224         err = mutex_lock_interruptible(&rng_mutex);
225         if (err)
226                 return -ERESTARTSYS;
227         buf[0] = '\0';
228         list_for_each_entry(rng, &rng_list, list) {
229                 strncat(buf, rng->name, PAGE_SIZE - ret - 1);
230                 ret += strlen(rng->name);
231                 strncat(buf, " ", PAGE_SIZE - ret - 1);
232                 ret++;
233         }
234         strncat(buf, "\n", PAGE_SIZE - ret - 1);
235         ret++;
236         mutex_unlock(&rng_mutex);
237
238         return ret;
239 }
240
241 static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
242                    hwrng_attr_current_show,
243                    hwrng_attr_current_store);
244 static DEVICE_ATTR(rng_available, S_IRUGO,
245                    hwrng_attr_available_show,
246                    NULL);
247
248
249 static void unregister_miscdev(void)
250 {
251         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
252         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
253         misc_deregister(&rng_miscdev);
254 }
255
256 static int register_miscdev(void)
257 {
258         int err;
259
260         err = misc_register(&rng_miscdev);
261         if (err)
262                 goto out;
263         err = device_create_file(rng_miscdev.this_device,
264                                  &dev_attr_rng_current);
265         if (err)
266                 goto err_misc_dereg;
267         err = device_create_file(rng_miscdev.this_device,
268                                  &dev_attr_rng_available);
269         if (err)
270                 goto err_remove_current;
271 out:
272         return err;
273
274 err_remove_current:
275         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
276 err_misc_dereg:
277         misc_deregister(&rng_miscdev);
278         goto out;
279 }
280
281 int hwrng_register(struct hwrng *rng)
282 {
283         int must_register_misc;
284         int err = -EINVAL;
285         struct hwrng *old_rng, *tmp;
286
287         if (rng->name == NULL ||
288             rng->data_read == NULL)
289                 goto out;
290
291         mutex_lock(&rng_mutex);
292
293         /* Must not register two RNGs with the same name. */
294         err = -EEXIST;
295         list_for_each_entry(tmp, &rng_list, list) {
296                 if (strcmp(tmp->name, rng->name) == 0)
297                         goto out_unlock;
298         }
299
300         must_register_misc = (current_rng == NULL);
301         old_rng = current_rng;
302         if (!old_rng) {
303                 err = hwrng_init(rng);
304                 if (err)
305                         goto out_unlock;
306                 current_rng = rng;
307         }
308         err = 0;
309         if (must_register_misc) {
310                 err = register_miscdev();
311                 if (err) {
312                         if (!old_rng) {
313                                 hwrng_cleanup(rng);
314                                 current_rng = NULL;
315                         }
316                         goto out_unlock;
317                 }
318         }
319         INIT_LIST_HEAD(&rng->list);
320         list_add_tail(&rng->list, &rng_list);
321 out_unlock:
322         mutex_unlock(&rng_mutex);
323 out:
324         return err;
325 }
326 EXPORT_SYMBOL_GPL(hwrng_register);
327
328 void hwrng_unregister(struct hwrng *rng)
329 {
330         int err;
331
332         mutex_lock(&rng_mutex);
333
334         list_del(&rng->list);
335         if (current_rng == rng) {
336                 hwrng_cleanup(rng);
337                 if (list_empty(&rng_list)) {
338                         current_rng = NULL;
339                 } else {
340                         current_rng = list_entry(rng_list.prev, struct hwrng, list);
341                         err = hwrng_init(current_rng);
342                         if (err)
343                                 current_rng = NULL;
344                 }
345         }
346         if (list_empty(&rng_list))
347                 unregister_miscdev();
348
349         mutex_unlock(&rng_mutex);
350 }
351 EXPORT_SYMBOL_GPL(hwrng_unregister);
352
353
354 MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
355 MODULE_LICENSE("GPL");