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