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