Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.