Merge git://git.infradead.org/~dedekind/ubi-2.6
[pandora-kernel.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/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, int wait)
70 {
71         if (!rng->data_present)
72                 return 1;
73         return rng->data_present(rng, wait);
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 err = 0;
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
110                 bytes_read = 0;
111                 if (hwrng_data_present(current_rng,
112                                        !(filp->f_flags & O_NONBLOCK)))
113                         bytes_read = hwrng_data_read(current_rng, &data);
114                 mutex_unlock(&rng_mutex);
115
116                 err = -EAGAIN;
117                 if (!bytes_read && (filp->f_flags & O_NONBLOCK))
118                         goto out;
119                 if (bytes_read < 0) {
120                         err = bytes_read;
121                         goto out;
122                 }
123
124                 err = -EFAULT;
125                 while (bytes_read && size) {
126                         if (put_user((u8)data, buf++))
127                                 goto out;
128                         size--;
129                         ret++;
130                         bytes_read--;
131                         data >>= 8;
132                 }
133
134                 if (need_resched())
135                         schedule_timeout_interruptible(1);
136                 err = -ERESTARTSYS;
137                 if (signal_pending(current))
138                         goto out;
139         }
140 out:
141         return ret ? : err;
142 }
143
144
145 static const struct file_operations rng_chrdev_ops = {
146         .owner          = THIS_MODULE,
147         .open           = rng_dev_open,
148         .read           = rng_dev_read,
149 };
150
151 static struct miscdevice rng_miscdev = {
152         .minor          = RNG_MISCDEV_MINOR,
153         .name           = RNG_MODULE_NAME,
154         .fops           = &rng_chrdev_ops,
155 };
156
157
158 static ssize_t hwrng_attr_current_store(struct device *dev,
159                                         struct device_attribute *attr,
160                                         const char *buf, size_t len)
161 {
162         int err;
163         struct hwrng *rng;
164
165         err = mutex_lock_interruptible(&rng_mutex);
166         if (err)
167                 return -ERESTARTSYS;
168         err = -ENODEV;
169         list_for_each_entry(rng, &rng_list, list) {
170                 if (strcmp(rng->name, buf) == 0) {
171                         if (rng == current_rng) {
172                                 err = 0;
173                                 break;
174                         }
175                         err = hwrng_init(rng);
176                         if (err)
177                                 break;
178                         hwrng_cleanup(current_rng);
179                         current_rng = rng;
180                         err = 0;
181                         break;
182                 }
183         }
184         mutex_unlock(&rng_mutex);
185
186         return err ? : len;
187 }
188
189 static ssize_t hwrng_attr_current_show(struct device *dev,
190                                        struct device_attribute *attr,
191                                        char *buf)
192 {
193         int err;
194         ssize_t ret;
195         const char *name = "none";
196
197         err = mutex_lock_interruptible(&rng_mutex);
198         if (err)
199                 return -ERESTARTSYS;
200         if (current_rng)
201                 name = current_rng->name;
202         ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
203         mutex_unlock(&rng_mutex);
204
205         return ret;
206 }
207
208 static ssize_t hwrng_attr_available_show(struct device *dev,
209                                          struct device_attribute *attr,
210                                          char *buf)
211 {
212         int err;
213         ssize_t ret = 0;
214         struct hwrng *rng;
215
216         err = mutex_lock_interruptible(&rng_mutex);
217         if (err)
218                 return -ERESTARTSYS;
219         buf[0] = '\0';
220         list_for_each_entry(rng, &rng_list, list) {
221                 strncat(buf, rng->name, PAGE_SIZE - ret - 1);
222                 ret += strlen(rng->name);
223                 strncat(buf, " ", PAGE_SIZE - ret - 1);
224                 ret++;
225         }
226         strncat(buf, "\n", PAGE_SIZE - ret - 1);
227         ret++;
228         mutex_unlock(&rng_mutex);
229
230         return ret;
231 }
232
233 static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
234                    hwrng_attr_current_show,
235                    hwrng_attr_current_store);
236 static DEVICE_ATTR(rng_available, S_IRUGO,
237                    hwrng_attr_available_show,
238                    NULL);
239
240
241 static void unregister_miscdev(void)
242 {
243         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
244         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
245         misc_deregister(&rng_miscdev);
246 }
247
248 static int register_miscdev(void)
249 {
250         int err;
251
252         err = misc_register(&rng_miscdev);
253         if (err)
254                 goto out;
255         err = device_create_file(rng_miscdev.this_device,
256                                  &dev_attr_rng_current);
257         if (err)
258                 goto err_misc_dereg;
259         err = device_create_file(rng_miscdev.this_device,
260                                  &dev_attr_rng_available);
261         if (err)
262                 goto err_remove_current;
263 out:
264         return err;
265
266 err_remove_current:
267         device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
268 err_misc_dereg:
269         misc_deregister(&rng_miscdev);
270         goto out;
271 }
272
273 int hwrng_register(struct hwrng *rng)
274 {
275         int must_register_misc;
276         int err = -EINVAL;
277         struct hwrng *old_rng, *tmp;
278
279         if (rng->name == NULL ||
280             rng->data_read == NULL)
281                 goto out;
282
283         mutex_lock(&rng_mutex);
284
285         /* Must not register two RNGs with the same name. */
286         err = -EEXIST;
287         list_for_each_entry(tmp, &rng_list, list) {
288                 if (strcmp(tmp->name, rng->name) == 0)
289                         goto out_unlock;
290         }
291
292         must_register_misc = (current_rng == NULL);
293         old_rng = current_rng;
294         if (!old_rng) {
295                 err = hwrng_init(rng);
296                 if (err)
297                         goto out_unlock;
298                 current_rng = rng;
299         }
300         err = 0;
301         if (must_register_misc) {
302                 err = register_miscdev();
303                 if (err) {
304                         if (!old_rng) {
305                                 hwrng_cleanup(rng);
306                                 current_rng = NULL;
307                         }
308                         goto out_unlock;
309                 }
310         }
311         INIT_LIST_HEAD(&rng->list);
312         list_add_tail(&rng->list, &rng_list);
313 out_unlock:
314         mutex_unlock(&rng_mutex);
315 out:
316         return err;
317 }
318 EXPORT_SYMBOL_GPL(hwrng_register);
319
320 void hwrng_unregister(struct hwrng *rng)
321 {
322         int err;
323
324         mutex_lock(&rng_mutex);
325
326         list_del(&rng->list);
327         if (current_rng == rng) {
328                 hwrng_cleanup(rng);
329                 if (list_empty(&rng_list)) {
330                         current_rng = NULL;
331                 } else {
332                         current_rng = list_entry(rng_list.prev, struct hwrng, list);
333                         err = hwrng_init(current_rng);
334                         if (err)
335                                 current_rng = NULL;
336                 }
337         }
338         if (list_empty(&rng_list))
339                 unregister_miscdev();
340
341         mutex_unlock(&rng_mutex);
342 }
343 EXPORT_SYMBOL_GPL(hwrng_unregister);
344
345
346 MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
347 MODULE_LICENSE("GPL");