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