File: /usr/src/linux/drivers/char/i810_rng.c
1 /*
2
3 Hardware driver for Intel i810 Random Number Generator (RNG)
4 Copyright 2000,2001 Jeff Garzik <jgarzik@mandrakesoft.com>
5 Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
6
7 Driver Web site: http://sourceforge.net/projects/gkernel/
8
9 Please read Documentation/i810_rng.txt for details on use.
10
11 ----------------------------------------------------------
12
13 This software may be used and distributed according to the terms
14 of the GNU General Public License, incorporated herein by reference.
15
16 */
17
18
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/fs.h>
22 #include <linux/init.h>
23 #include <linux/pci.h>
24 #include <linux/interrupt.h>
25 #include <linux/spinlock.h>
26 #include <linux/random.h>
27 #include <linux/miscdevice.h>
28 #include <linux/smp_lock.h>
29 #include <linux/mm.h>
30
31 #include <asm/io.h>
32 #include <asm/uaccess.h>
33
34
35 /*
36 * core module and version information
37 */
38 #define RNG_VERSION "0.9.6"
39 #define RNG_MODULE_NAME "i810_rng"
40 #define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
41 #define PFX RNG_MODULE_NAME ": "
42
43
44 /*
45 * debugging macros
46 */
47 #undef RNG_DEBUG /* define to enable copious debugging info */
48
49 #ifdef RNG_DEBUG
50 /* note: prints function name for you */
51 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
52 #else
53 #define DPRINTK(fmt, args...)
54 #endif
55
56 #undef RNG_NDEBUG /* define to disable lightweight runtime checks */
57 #ifdef RNG_NDEBUG
58 #define assert(expr)
59 #else
60 #define assert(expr) \
61 if(!(expr)) { \
62 printk( "Assertion failed! %s,%s,%s,line=%d\n", \
63 #expr,__FILE__,__FUNCTION__,__LINE__); \
64 }
65 #endif
66
67
68 /*
69 * RNG registers (offsets from rng_mem)
70 */
71 #define RNG_HW_STATUS 0
72 #define RNG_PRESENT 0x40
73 #define RNG_ENABLED 0x01
74 #define RNG_STATUS 1
75 #define RNG_DATA_PRESENT 0x01
76 #define RNG_DATA 2
77
78 /*
79 * Magic address at which Intel PCI bridges locate the RNG
80 */
81 #define RNG_ADDR 0xFFBC015F
82 #define RNG_ADDR_LEN 3
83
84 #define RNG_MISCDEV_MINOR 183 /* official */
85
86 /*
87 * various RNG status variables. they are globals
88 * as we only support a single RNG device
89 */
90 static void *rng_mem; /* token to our ioremap'd RNG register area */
91 static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */
92
93
94 /*
95 * inlined helper functions for accessing RNG registers
96 */
97 static inline u8 rng_hwstatus (void)
98 {
99 assert (rng_mem != NULL);
100 return readb (rng_mem + RNG_HW_STATUS);
101 }
102
103 static inline u8 rng_hwstatus_set (u8 hw_status)
104 {
105 assert (rng_mem != NULL);
106 writeb (hw_status, rng_mem + RNG_HW_STATUS);
107 return rng_hwstatus ();
108 }
109
110
111 static inline int rng_data_present (void)
112 {
113 assert (rng_mem != NULL);
114
115 return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0;
116 }
117
118
119 static inline int rng_data_read (void)
120 {
121 assert (rng_mem != NULL);
122
123 return readb (rng_mem + RNG_DATA);
124 }
125
126 /*
127 * rng_enable - enable the RNG hardware
128 */
129
130 static int rng_enable (void)
131 {
132 int rc = 0;
133 u8 hw_status, new_status;
134
135 DPRINTK ("ENTER\n");
136
137 hw_status = rng_hwstatus ();
138
139 if ((hw_status & RNG_ENABLED) == 0) {
140 new_status = rng_hwstatus_set (hw_status | RNG_ENABLED);
141
142 if (new_status & RNG_ENABLED)
143 printk (KERN_INFO PFX "RNG h/w enabled\n");
144 else {
145 printk (KERN_ERR PFX "Unable to enable the RNG\n");
146 rc = -EIO;
147 }
148 }
149
150 DPRINTK ("EXIT, returning %d\n", rc);
151 return rc;
152 }
153
154 /*
155 * rng_disable - disable the RNG hardware
156 */
157
158 static void rng_disable(void)
159 {
160 u8 hw_status, new_status;
161
162 DPRINTK ("ENTER\n");
163
164 hw_status = rng_hwstatus ();
165
166 if (hw_status & RNG_ENABLED) {
167 new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
168
169 if ((new_status & RNG_ENABLED) == 0)
170 printk (KERN_INFO PFX "RNG h/w disabled\n");
171 else {
172 printk (KERN_ERR PFX "Unable to disable the RNG\n");
173 }
174 }
175
176 DPRINTK ("EXIT\n");
177 }
178
179 static int rng_dev_open (struct inode *inode, struct file *filp)
180 {
181 int rc;
182
183 if ((filp->f_mode & FMODE_READ) == 0)
184 return -EINVAL;
185 if (filp->f_mode & FMODE_WRITE)
186 return -EINVAL;
187
188 /* wait for device to become free */
189 if (filp->f_flags & O_NONBLOCK) {
190 if (down_trylock (&rng_open_sem))
191 return -EAGAIN;
192 } else {
193 if (down_interruptible (&rng_open_sem))
194 return -ERESTARTSYS;
195 }
196
197 rc = rng_enable ();
198 if (rc) {
199 up (&rng_open_sem);
200 return rc;
201 }
202
203 return 0;
204 }
205
206
207 static int rng_dev_release (struct inode *inode, struct file *filp)
208 {
209 rng_disable ();
210 up (&rng_open_sem);
211 return 0;
212 }
213
214
215 static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
216 loff_t * offp)
217 {
218 static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
219 int have_data;
220 u8 data = 0;
221 ssize_t ret = 0;
222
223 while (size) {
224 spin_lock (&rng_lock);
225
226 have_data = 0;
227 if (rng_data_present ()) {
228 data = rng_data_read ();
229 have_data = 1;
230 }
231
232 spin_unlock (&rng_lock);
233
234 if (have_data) {
235 if (put_user (data, buf++)) {
236 ret = ret ? : -EFAULT;
237 break;
238 }
239 size--;
240 ret++;
241 }
242
243 if (filp->f_flags & O_NONBLOCK)
244 return ret ? : -EAGAIN;
245
246 current->state = TASK_INTERRUPTIBLE;
247 schedule_timeout(1);
248
249 if (signal_pending (current))
250 return ret ? : -ERESTARTSYS;
251 }
252
253 return ret;
254 }
255
256
257 static struct file_operations rng_chrdev_ops = {
258 owner: THIS_MODULE,
259 open: rng_dev_open,
260 release: rng_dev_release,
261 read: rng_dev_read,
262 };
263
264
265 static struct miscdevice rng_miscdev = {
266 RNG_MISCDEV_MINOR,
267 RNG_MODULE_NAME,
268 &rng_chrdev_ops,
269 };
270
271
272 /*
273 * rng_init_one - look for and attempt to init a single RNG
274 */
275 static int __init rng_init_one (struct pci_dev *dev)
276 {
277 int rc;
278 u8 hw_status;
279
280 DPRINTK ("ENTER\n");
281
282 rc = misc_register (&rng_miscdev);
283 if (rc) {
284 printk (KERN_ERR PFX "cannot register misc device\n");
285 DPRINTK ("EXIT, returning %d\n", rc);
286 goto err_out;
287 }
288
289 rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN);
290 if (rng_mem == NULL) {
291 printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
292 DPRINTK ("EXIT, returning -EBUSY\n");
293 rc = -EBUSY;
294 goto err_out_free_miscdev;
295 }
296
297 /* Check for Intel 82802 */
298 hw_status = rng_hwstatus ();
299 if ((hw_status & RNG_PRESENT) == 0) {
300 printk (KERN_ERR PFX "RNG not detected\n");
301 DPRINTK ("EXIT, returning -ENODEV\n");
302 rc = -ENODEV;
303 goto err_out_free_map;
304 }
305
306 /* turn RNG h/w off, if it's on */
307 if (hw_status & RNG_ENABLED)
308 hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
309 if (hw_status & RNG_ENABLED) {
310 printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
311 goto err_out_free_map;
312 }
313
314 DPRINTK ("EXIT, returning 0\n");
315 return 0;
316
317 err_out_free_map:
318 iounmap (rng_mem);
319 err_out_free_miscdev:
320 misc_deregister (&rng_miscdev);
321 err_out:
322 return rc;
323 }
324
325
326 /*
327 * Data for PCI driver interface
328 *
329 * This data only exists for exporting the supported
330 * PCI ids via MODULE_DEVICE_TABLE. We do not actually
331 * register a pci_driver, because someone else might one day
332 * want to register another driver on the same PCI id.
333 */
334 static struct pci_device_id rng_pci_tbl[] __initdata = {
335 { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
336 { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
337 { 0x8086, 0x1130, PCI_ANY_ID, PCI_ANY_ID, },
338 { 0, },
339 };
340 MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
341
342
343 MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek");
344 MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver");
345
346
347 /*
348 * rng_init - initialize RNG module
349 */
350 static int __init rng_init (void)
351 {
352 int rc;
353 struct pci_dev *pdev;
354
355 DPRINTK ("ENTER\n");
356
357 init_MUTEX (&rng_open_sem);
358
359 pci_for_each_dev(pdev) {
360 if (pci_match_device (rng_pci_tbl, pdev) != NULL)
361 goto match;
362 }
363
364 DPRINTK ("EXIT, returning -ENODEV\n");
365 return -ENODEV;
366
367 match:
368 rc = rng_init_one (pdev);
369 if (rc)
370 return rc;
371
372 printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
373
374 DPRINTK ("EXIT, returning 0\n");
375 return 0;
376 }
377
378
379 /*
380 * rng_init - shutdown RNG module
381 */
382 static void __exit rng_cleanup (void)
383 {
384 DPRINTK ("ENTER\n");
385
386 misc_deregister (&rng_miscdev);
387
388 iounmap (rng_mem);
389
390 DPRINTK ("EXIT\n");
391 }
392
393
394 module_init (rng_init);
395 module_exit (rng_cleanup);
396