1 /*
2 * short.c -- Simple Hardware Operations and Raw Tests
3 * short.c -- also a brief example of interrupt handling ("short int")
4 *
5 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
6 * Copyright (C) 2001 O'Reilly & Associates
7 *
8 * The source code in this file can be freely used, adapted,
9 * and redistributed in source or binary form, so long as an
10 * acknowledgment appears in derived source files. The citation
11 * should list that the code comes from the book "Linux Device
12 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
13 * by O'Reilly & Associates. No warranty is attached;
14 * we cannot take responsibility for errors or fitness for use.
15 *
16 * $Id: short.c,v 1.16 2004/10/29 16:45:40 corbet Exp $
17 */
18
19 /*
20 * FIXME: this driver is not safe with concurrent readers or
21 * writers.
22 */
23
24 #include <linux/module.h>
25 #include <linux/moduleparam.h>
26 #include <linux/init.h>
27
28 #include <linux/sched.h>
29 #include <linux/kernel.h> /* printk() */
30 #include <linux/fs.h> /* everything... */
31 #include <linux/errno.h> /* error codes */
32 #include <linux/delay.h> /* udelay */
33 #include <linux/kdev_t.h>
34 #include <linux/slab.h>
35 #include <linux/mm.h>
36 #include <linux/ioport.h>
37 #include <linux/interrupt.h>
38 #include <linux/workqueue.h>
39 #include <linux/poll.h>
40 #include <linux/wait.h>
41
42 #include <asm/io.h>
43
44 #define SHORT_NR_PORTS 8 /* use 8 ports by default */
45
46 /*
47 * all of the parameters have no "short_" prefix, to save typing when
48 * specifying them at load time
49 */
50 static int major = 0; /* dynamic by default */
51 module_param(major, int, 0);
52
53 static int use_mem = 0; /* default is I/O-mapped */
54 module_param(use_mem, int, 0);
55
56 /* default is the first printer port on PC's. "short_base" is there too
57 because it's what we want to use in the code */
58 static unsigned long base = 0x378;
59 unsigned long short_base = 0;
60 module_param(base, long, 0);
61
62 /* The interrupt line is undefined by default. "short_irq" is as above */
63 static int irq = -1;
64 volatile int short_irq = -1;
65 module_param(irq, int, 0);
66
67 static int probe = 0; /* select at load time how to probe irq line */
68 module_param(probe, int, 0);
69
70 static int wq = 0; /* select at load time whether a workqueue is used */
71 module_param(wq, int, 0);
72
73 static int tasklet = 0; /* select whether a tasklet is used */
74 module_param(tasklet, int, 0);
75
76 static int share = 0; /* select at load time whether install a shared irq */
77 module_param(share, int, 0);
78
79 MODULE_AUTHOR ("Alessandro Rubini");
80 MODULE_LICENSE("Dual BSD/GPL");
81
82
83 unsigned long short_buffer = 0;
84 unsigned long volatile short_head;
85 volatile unsigned long short_tail;
86 DECLARE_WAIT_QUEUE_HEAD(short_queue);
87
88 /* Set up our tasklet if we're doing that. */
89 void short_do_tasklet(unsigned long);
90 DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0);
91
92 /*
93 * Atomicly increment an index into short_buffer
94 */
95 static inline void short_incr_bp(volatile unsigned long *index, int delta)
96 {
97 unsigned long new = *index + delta;
98 barrier(); /* Don't optimize these two together */
99 *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new;
100 }
101
102
103 /*
104 * The devices with low minor numbers write/read burst of data to/from
105 * specific I/O ports (by default the parallel ones).
106 *
107 * The device with 128 as minor number returns ascii strings telling
108 * when interrupts have been received. Writing to the device toggles
109 * 00/FF on the parallel data lines. If there is a loopback wire, this
110 * generates interrupts.
111 */
112
113 int short_open (struct inode *inode, struct file *filp)
114 {
115 extern struct file_operations short_i_fops;
116
117 if (iminor (inode) & 0x80)
118 filp->f_op = &short_i_fops; /* the interrupt-driven node */
119 return 0;
120 }
121
122
123 int short_release (struct inode *inode, struct file *filp)
124 {
125 return 0;
126 }
127
128
129 /* first, the port-oriented device */
130
131 enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING, SHORT_MEMORY};
132
133 ssize_t do_short_read (struct inode *inode, struct file *filp, char __user *buf,
134 size_t count, loff_t *f_pos)
135 {
136 int retval = count, minor = iminor (inode);
137 unsigned long port = short_base + (minor&0x0f);
138 void *address = (void *) short_base + (minor&0x0f);
139 int mode = (minor&0x70) >> 4;
140 unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr;
141
142 if (!kbuf)
143 return -ENOMEM;
144 ptr = kbuf;
145
146 if (use_mem)
147 mode = SHORT_MEMORY;
148
149 switch(mode) {
150 case SHORT_STRING:
151 insb(port, ptr, count);
152 rmb();
153 break;
154
155 case SHORT_DEFAULT:
156 while (count--) {
157 *(ptr++) = inb(port);
158 rmb();
159 }
160 break;
161
162 case SHORT_MEMORY:
163 while (count--) {
164 *ptr++ = ioread8(address);
165 rmb();
166 }
167 break;
168 case SHORT_PAUSE:
169 while (count--) {
170 *(ptr++) = inb_p(port);
171 rmb();
172 }
173 break;
174
175 default: /* no more modes defined by now */
176 retval = -EINVAL;
177 break;
178 }
179 if ((retval > 0) && copy_to_user(buf, kbuf, retval))
180 retval = -EFAULT;
181 kfree(kbuf);
182 return retval;
183 }
184
185
186 /*
187 * Version-specific methods for the fops structure. FIXME don't need anymore.
188 */
189 ssize_t short_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
190 {
191 return do_short_read(filp->f_dentry->d_inode, filp, buf, count, f_pos);
192 }
193
194
195
196 ssize_t do_short_write (struct inode *inode, struct file *filp, const char __user *buf,
197 size_t count, loff_t *f_pos)
198 {
199 int retval = count, minor = iminor(inode);
200 unsigned long port = short_base + (minor&0x0f);
201 void *address = (void *) short_base + (minor&0x0f);
202 int mode = (minor&0x70) >> 4;
203 unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr;
204
205 if (!kbuf)
206 return -ENOMEM;
207 if (copy_from_user(kbuf, buf, count))
208 return -EFAULT;
209 ptr = kbuf;
210
211 if (use_mem)
212 mode = SHORT_MEMORY;
213
214 switch(mode) {
215 case SHORT_PAUSE:
216 while (count--) {
217 outb_p(*(ptr++), port);
218 wmb();
219 }
220 break;
221
222 case SHORT_STRING:
223 outsb(port, ptr, count);
224 wmb();
225 break;
226
227 case SHORT_DEFAULT:
228 while (count--) {
229 outb(*(ptr++), port);
230 wmb();
231 }
232 break;
233
234 case SHORT_MEMORY:
235 while (count--) {
236 iowrite8(*ptr++, address);
237 wmb();
238 }
239 break;
240
241 default: /* no more modes defined by now */
242 retval = -EINVAL;
243 break;
244 }
245 kfree(kbuf);
246 return retval;
247 }
248
249
250 ssize_t short_write(struct file *filp, const char __user *buf, size_t count,
251 loff_t *f_pos)
252 {
253 return do_short_write(filp->f_dentry->d_inode, filp, buf, count, f_pos);
254 }
255
256
257
258
259 unsigned int short_poll(struct file *filp, poll_table *wait)
260 {
261 return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
262 }
263
264
265
266
267
268
269 struct file_operations short_fops = {
270 .owner = THIS_MODULE,
271 .read = short_read,
272 .write = short_write,
273 .poll = short_poll,
274 .open = short_open,
275 .release = short_release,
276 };
277
278 /* then, the interrupt-related device */
279
280 ssize_t short_i_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
281 {
282 int count0;
283 DEFINE_WAIT(wait);
284
285 while (short_head == short_tail) {
286 prepare_to_wait(&short_queue, &wait, TASK_INTERRUPTIBLE);
287 if (short_head == short_tail)
288 schedule();
289 finish_wait(&short_queue, &wait);
290 if (signal_pending (current)) /* a signal arrived */
291 return -ERESTARTSYS; /* tell the fs layer to handle it */
292 }
293 /* count0 is the number of readable data bytes */
294 count0 = short_head - short_tail;
295 if (count0 < 0) /* wrapped */
296 count0 = short_buffer + PAGE_SIZE - short_tail;
297 if (count0 < count) count = count0;
298
299 if (copy_to_user(buf, (char *)short_tail, count))
300 return -EFAULT;
301 short_incr_bp (&short_tail, count);
302 return count;
303 }
304
305 ssize_t short_i_write (struct file *filp, const char __user *buf, size_t count,
306 loff_t *f_pos)
307 {
308 int written = 0, odd = *f_pos & 1;
309 unsigned long port = short_base; /* output to the parallel data latch */
310 void *address = (void *) short_base;
311
312 if (use_mem) {
313 while (written < count)
314 iowrite8(0xff * ((++written + odd) & 1), address);
315 } else {
316 while (written < count)
317 outb(0xff * ((++written + odd) & 1), port);
318 }
319
320 *f_pos += count;
321 return written;
322 }
323
324
325
326
327 struct file_operations short_i_fops = {
328 .owner = THIS_MODULE,
329 .read = short_i_read,
330 .write = short_i_write,
331 .open = short_open,
332 .release = short_release,
333 };
334
335 irqreturn_t short_interrupt(int irq, void *dev_id)
336 {
337 struct timeval tv;
338 int written;
339
340 do_gettimeofday(&tv);
341
342 /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */
343 written = sprintf((char *)short_head,"%08u.%06u\n",
344 (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
345 BUG_ON(written != 16);
346 short_incr_bp(&short_head, written);
347 wake_up_interruptible(&short_queue); /* awake any reading process */
348 return IRQ_HANDLED;
349 }
350
351 /*
352 * The following two functions are equivalent to the previous one,
353 * but split in top and bottom half. First, a few needed variables
354 */
355
356 #define NR_TIMEVAL 512 /* length of the array of time values */
357
358 struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */
359 volatile struct timeval *tv_head=tv_data;
360 volatile struct timeval *tv_tail=tv_data;
361
362 static struct work_struct short_wq;
363
364
365 int short_wq_count = 0;
366
367 /*
368 * Increment a circular buffer pointer in a way that nobody sees
369 * an intermediate value.
370 */
371 static inline void short_incr_tv(volatile struct timeval **tvp)
372 {
373 if (*tvp == (tv_data + NR_TIMEVAL - 1))
374 *tvp = tv_data; /* Wrap */
375 else
376 (*tvp)++;
377 }
378
379 void short_do_tasklet (unsigned long unused)
380 {
381 int savecount = short_wq_count, written;
382 short_wq_count = 0; /* we have already been removed from the queue */
383 /*
384 * The bottom half reads the tv array, filled by the top half,
385 * and prints it to the circular text buffer, which is then consumed
386 * by reading processes
387 */
388
389 /* First write the number of interrupts that occurred before this bh */
390 written = sprintf((char *)short_head,"bh after %6i\n",savecount);
391 short_incr_bp(&short_head, written);
392
393 /*
394 * Then, write the time values. Write exactly 16 bytes at a time,
395 * so it aligns with PAGE_SIZE
396 */
397
398 do {
399 written = sprintf((char *)short_head,"%08u.%06u\n",
400 (int)(tv_tail->tv_sec % 100000000),
401 (int)(tv_tail->tv_usec));
402 short_incr_bp(&short_head, written);
403 short_incr_tv(&tv_tail);
404 } while (tv_tail != tv_head);
405
406 wake_up_interruptible(&short_queue); /* awake any reading process */
407 }
408
409 irqreturn_t short_wq_interrupt(int irq, void *dev_id)
410 {
411 /* Grab the current time information. */
412 do_gettimeofday((struct timeval *) tv_head);
413 short_incr_tv(&tv_head);
414
415 /* Queue the bh. Don't worry about multiple enqueueing */
416 schedule_work(&short_wq);
417
418 short_wq_count++; /* record that an interrupt arrived */
419 return IRQ_HANDLED;
420 }
421
422 void short_do_work(struct work_struct *work)
423 {
424 short_do_tasklet(0);
425 }
426
427 /*
428 * Tasklet top half
429 */
430
431 irqreturn_t short_tl_interrupt(int irq, void *dev_id)
432 {
433 do_gettimeofday((struct timeval *) tv_head); /* cast to stop 'volatile' warning */
434 short_incr_tv(&tv_head);
435 tasklet_schedule(&short_tasklet);
436 short_wq_count++; /* record that an interrupt arrived */
437 return IRQ_HANDLED;
438 }
439
440 irqreturn_t short_sh_interrupt(int irq, void *dev_id)
441 {
442 int value, written;
443 struct timeval tv;
444
445 /* If it wasn't short, return immediately */
446 value = inb(short_base);
447 if (!(value & 0x80))
448 return IRQ_NONE;
449
450 /* clear the interrupting bit */
451 outb(value & 0x7F, short_base);
452
453 /* the rest is unchanged */
454
455 do_gettimeofday(&tv);
456 written = sprintf((char *)short_head,"%08u.%06u\n",
457 (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
458 short_incr_bp(&short_head, written);
459 wake_up_interruptible(&short_queue); /* awake any reading process */
460 return IRQ_HANDLED;
461 }
462
463 void short_kernelprobe(void)
464 {
465 int count = 0;
466 do {
467 unsigned long mask;
468
469 mask = probe_irq_on();
470 outb_p(0x10,short_base+2); /* enable reporting */
471 outb_p(0x00,short_base); /* clear the bit */
472 outb_p(0xFF,short_base); /* set the bit: interrupt! */
473 outb_p(0x00,short_base+2); /* disable reporting */
474 udelay(5); /* give it some time */
475 short_irq = probe_irq_off(mask);
476
477 if (short_irq == 0) { /* none of them? */
478 printk(KERN_INFO "short: no irq reported by probe\n");
479 short_irq = -1;
480 }
481 /*
482 * if more than one line has been activated, the result is
483 * negative. We should service the interrupt (no need for lpt port)
484 * and loop over again. Loop at most five times, then give up
485 */
486 } while (short_irq < 0 && count++ < 5);
487 if (short_irq < 0)
488 printk("short: probe failed %i times, giving up\n", count);
489 }
490
491 irqreturn_t short_probing(int irq, void *dev_id)
492 {
493 if (short_irq == 0) short_irq = irq; /* found */
494 if (short_irq != irq) short_irq = -irq; /* ambiguous */
495 return IRQ_HANDLED;
496 }
497
498 void short_selfprobe(void)
499 {
500 int trials[] = {3, 5, 7, 9, 0};
501 int tried[] = {0, 0, 0, 0, 0};
502 int i, count = 0;
503
504 /*
505 * install the probing handler for all possible lines. Remember
506 * the result (0 for success, or -EBUSY) in order to only free
507 * what has been acquired
508 */
509 for (i = 0; trials[i]; i++)
510 tried[i] = request_irq(trials[i], short_probing,
511 IRQ_DISABLED, "short probe", NULL);
512 do {
513 short_irq = 0; /* none got, yet */
514 outb_p(0x10,short_base+2); /* enable */
515 outb_p(0x00,short_base);
516 outb_p(0xFF,short_base); /* toggle the bit */
517 outb_p(0x00,short_base+2); /* disable */
518 udelay(5); /* give it some time */
519
520 /* the value has been set by the handler */
521 if (short_irq == 0) { /* none of them? */
522 printk(KERN_INFO "short: no irq reported by probe\n");
523 }
524 /*
525 * If more than one line has been activated, the result is
526 * negative. We should service the interrupt (but the lpt port
527 * doesn't need it) and loop over again. Do it at most 5 times
528 */
529 } while (short_irq <=0 && count++ < 5);
530
531 /* end of loop, uninstall the handler */
532 for (i = 0; trials[i]; i++)
533 if (tried[i] == 0)
534 free_irq(trials[i], NULL);
535
536 if (short_irq < 0)
537 printk("short: probe failed %i times, giving up\n", count);
538 }
539
540
541
542 /* Finally, init and cleanup */
543
544 int short_init(void)
545 {
546 int result;
547
548 /*
549 * first, sort out the base/short_base ambiguity: we'd better
550 * use short_base in the code, for clarity, but allow setting
551 * just "base" at load time. Same for "irq".
552 */
553 short_base = base;
554 short_irq = irq;
555
556 /* Get our needed resources. */
557 if (!use_mem) {
558 if (! request_region(short_base, SHORT_NR_PORTS, "short")) {
559 printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",
560 short_base);
561 return -ENODEV;
562 }
563
564 } else {
565 if (! request_mem_region(short_base, SHORT_NR_PORTS, "short")) {
566 printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n",
567 short_base);
568 return -ENODEV;
569 }
570
571 /* also, ioremap it */
572 short_base = (unsigned long) ioremap(short_base, SHORT_NR_PORTS);
573 /* Hmm... we should check the return value */
574 }
575 /* Here we register our device - should not fail thereafter */
576 result = register_chrdev(major, "short", &short_fops);
577 if (result < 0) {
578 printk(KERN_INFO "short: can't get major number\n");
579 release_region(short_base,SHORT_NR_PORTS); /* FIXME - use-mem case? */
580 return result;
581 }
582 if (major == 0) major = result; /* dynamic */
583
584 short_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */ /* FIXME */
585 short_head = short_tail = short_buffer;
586
587 /*
588 * Fill the workqueue structure, used for the bottom half handler.
589 * The cast is there to prevent warnings about the type of the
590 * (unused) argument.
591 */
592 /* this line is in short_init() */
593 INIT_WORK(&short_wq, short_do_work);
594
595 /*
596 * Now we deal with the interrupt: either kernel-based
597 * autodetection, DIY detection or default number
598 */
599
600 if (short_irq < 0 && probe == 1)
601 short_kernelprobe();
602
603 if (short_irq < 0 && probe == 2)
604 short_selfprobe();
605
606 if (short_irq < 0) /* not yet specified: force the default on */
607 switch(short_base) {
608 case 0x378: short_irq = 7; break;
609 case 0x278: short_irq = 2; break;
610 case 0x3bc: short_irq = 5; break;
611 }
612
613 /*
614 * If shared has been specified, installed the shared handler
615 * instead of the normal one. Do it first, before a -EBUSY will
616 * force short_irq to -1.
617 */
618 if (short_irq >= 0 && share > 0) {
619 result = request_irq(short_irq, short_sh_interrupt,
620 IRQF_SHARED | IRQF_DISABLED,"short",
621 short_sh_interrupt);
622 if (result) {
623 printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq);
624 short_irq = -1;
625 }
626 else { /* actually enable it -- assume this *is* a parallel port */
627 outb(0x10, short_base+2);
628 }
629 return 0; /* the rest of the function only installs handlers */
630 }
631
632 if (short_irq >= 0) {
633 result = request_irq(short_irq, short_interrupt,
634 IRQF_DISABLED, "short", NULL);
635 if (result) {
636 printk(KERN_INFO "short: can't get assigned irq %i\n",
637 short_irq);
638 short_irq = -1;
639 }
640 else { /* actually enable it -- assume this *is* a parallel port */
641 outb(0x10,short_base+2);
642 }
643 }
644
645 /*
646 * Ok, now change the interrupt handler if using top/bottom halves
647 * has been requested
648 */
649 if (short_irq >= 0 && (wq + tasklet) > 0) {
650 free_irq(short_irq,NULL);
651 result = request_irq(short_irq,
652 tasklet ? short_tl_interrupt :
653 short_wq_interrupt,
654 IRQF_DISABLED, "short-bh", NULL);
655 if (result) {
656 printk(KERN_INFO "short-bh: can't get assigned irq %i\n",
657 short_irq);
658 short_irq = -1;
659 }
660 }
661
662 return 0;
663 }
664
665 void short_cleanup(void)
666 {
667 if (short_irq >= 0) {
668 outb(0x0, short_base + 2); /* disable the interrupt */
669 if (!share) free_irq(short_irq, NULL);
670 else free_irq(short_irq, short_sh_interrupt);
671 }
672 /* Make sure we don't leave work queue/tasklet functions running */
673 if (tasklet)
674 tasklet_disable(&short_tasklet);
675 else
676 flush_scheduled_work();
677 unregister_chrdev(major, "short");
678 if (use_mem) {
679 iounmap((void __iomem *)short_base);
680 release_mem_region(short_base, SHORT_NR_PORTS);
681 } else {
682 release_region(short_base,SHORT_NR_PORTS);
683 }
684 if (short_buffer) free_page(short_buffer);
685 }
686
687 module_init(short_init);
688 module_exit(short_cleanup);
689
|
This page was automatically generated by the
LXR engine.
|