[PATCH] make more file_operation structs static
[linux-2.6.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 class_device *class,
166                                         const char *buf, size_t len)
167 {
168         int err;
169         struct hwrng *rng;
170
171         err = mutex_lock_interruptible(&rng_mutex);
172         if (err)
173                 return -ERESTARTSYS;
174         err = -ENODEV;
175         list_for_each_entry(rng, &rng_list, list) {
176                 if (strcmp(rng->name, buf) == 0) {
177                         if (rng == current_rng) {
178                                 err = 0;
179                                 break;
180                         }
181                         err = hwrng_init(rng);
182                         if (err)
183                                 break;
184                         hwrng_cleanup(current_rng);
185                         current_rng = rng;
186                         err = 0;
187                         break;
188                 }
189         }
190         mutex_unlock(&rng_mutex);
191
192         return err ? : len;
193 }
194
195 static ssize_t hwrng_attr_current_show(struct class_device *class,
196                                        char *buf)
197 {
198         int err;
199         ssize_t ret;
200         const char *name = "none";
201
202         err = mutex_lock_interruptible(&rng_mutex);
203         if (err)
204                 return -ERESTARTSYS;
205         if (current_rng)
206                 name = current_rng->name;
207         ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
208         mutex_unlock(&rng_mutex);
209
210         return ret;
211 }
212
213 static ssize_t hwrng_attr_available_show(struct class_device *class,
214                                          char *buf)
215 {
216         int err;
217         ssize_t ret = 0;
218         struct hwrng *rng;
219
220         err = mutex_lock_interruptible(&rng_mutex);
221         if (err)
222                 return -ERESTARTSYS;
223         buf[0] = '\0';
224         list_for_each_entry(rng, &rng_list, list) {
225                 strncat(buf, rng->name, PAGE_SIZE - ret - 1);
226                 ret += strlen(rng->name);
227                 strncat(buf, " ", PAGE_SIZE - ret - 1);
228                 ret++;
229         }
230         strncat(buf, "\n", PAGE_SIZE - ret - 1);
231         ret++;
232         mutex_unlock(&rng_mutex);
233
234         return ret;
235 }
236
237 static CLASS_DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
238                          hwrng_attr_current_show,
239                          hwrng_attr_current_store);
240 static CLASS_DEVICE_ATTR(rng_available, S_IRUGO,
241                          hwrng_attr_available_show,
242                          NULL);
243
244
245 static void unregister_miscdev(void)
246 {
247         class_device_remove_file(rng_miscdev.class,
248                                  &class_device_attr_rng_available);
249         class_device_remove_file(rng_miscdev.class,
250                                  &class_device_attr_rng_current);
251         misc_deregister(&rng_miscdev);
252 }
253
254 static int register_miscdev(void)
255 {
256         int err;
257
258         err = misc_register(&rng_miscdev);
259         if (err)
260                 goto out;
261         err = class_device_create_file(rng_miscdev.class,
262                                        &class_device_attr_rng_current);
263         if (err)
264                 goto err_misc_dereg;
265         err = class_device_create_file(rng_miscdev.class,
266                                        &class_device_attr_rng_available);
267         if (err)
268                 goto err_remove_current;
269 out:
270         return err;
271
272 err_remove_current:
273         class_device_remove_file(rng_miscdev.class,
274                                  &class_device_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");