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