📄 short.c
字号:
};void short_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct timeval tv; int written; do_gettimeofday(&tv); /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */ written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); short_incr_bp(&short_head, written); wake_up_interruptible(&short_queue); /* awake any reading process */}/* * The following two functions are equivalent to the previous one, * but split in top and bottom half. First, a few needed variables */#define NR_TIMEVAL 512 /* length of the array of time values */struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */volatile struct timeval *tv_head=tv_data;volatile struct timeval *tv_tail=tv_data;static struct tq_struct short_task; /* 0 by now, filled by short_init code */int short_bh_count = 0;/* * Increment a circular buffer pointer in a way that nobody sees * an intermediate value. */static inline void short_incr_tv(volatile struct timeval **tvp){ if (*tvp == (tv_data + NR_TIMEVAL - 1)) *tvp = tv_data; /* Wrap */ else (*tvp)++;}void short_do_tasklet (unsigned long unused){ int savecount = short_bh_count, written; short_bh_count = 0; /* we have already been removed from the queue */ /* * The bottom half reads the tv array, filled by the top half, * and prints it to the circular text buffer, which is then consumed * by reading processes */ /* First write the number of interrupts that occurred before this bh */ written = sprintf((char *)short_head,"bh after %6i\n",savecount); short_incr_bp(&short_head, written); /* * Then, write the time values. Write exactly 16 bytes at a time, * so it aligns with PAGE_SIZE */ do { written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv_tail->tv_sec % 100000000), (int)(tv_tail->tv_usec)); short_incr_bp(&short_head, written); short_incr_tv(&tv_tail); } while (tv_tail != tv_head); wake_up_interruptible(&short_queue); /* awake any reading process */}void short_bh_interrupt(int irq, void *dev_id, struct pt_regs *regs){ /* cast to stop 'volatile' warning */ do_gettimeofday((struct timeval *) tv_head); short_incr_tv(&tv_head); /* Queue the bh. Don't care for multiple enqueueing */ queue_task(&short_task, &tq_immediate); mark_bh(IMMEDIATE_BH); short_bh_count++; /* record that an interrupt arrived */}#ifdef HAVE_TASKLETS/* * Tasklet top half */void short_tl_interrupt(int irq, void *dev_id, struct pt_regs *regs){ do_gettimeofday((struct timeval *) tv_head); /* cast to stop 'volatile' warning */ short_incr_tv(&tv_head); tasklet_schedule(&short_tasklet); short_bh_count++; /* record that an interrupt arrived */}#endif /* HAVE_TASKLETS */void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int value, written; struct timeval tv; /* If it wasn't short, return immediately */ value = inb(short_base); if (!(value & 0x80)) return; /* clear the interrupting bit */ outb(value & 0x7F, short_base); /* the rest is unchanged */ do_gettimeofday(&tv); written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); short_incr_bp(&short_head, written); wake_up_interruptible(&short_queue); /* awake any reading process */}void short_kernelprobe(void){ int count = 0; do { unsigned long mask; mask = probe_irq_on(); outb_p(0x10,short_base+2); /* enable reporting */ outb_p(0x00,short_base); /* clear the bit */ outb_p(0xFF,short_base); /* set the bit: interrupt! */ outb_p(0x00,short_base+2); /* disable reporting */ udelay(5); /* give it some time */ short_irq = probe_irq_off(mask); if (short_irq == 0) { /* none of them? */ printk(KERN_INFO "short: no irq reported by probe\n"); short_irq = -1; } /* * if more than one line has been activated, the result is * negative. We should service the interrupt (no need for lpt port) * and loop over again. Loop at most five times, then give up */ } while (short_irq < 0 && count++ < 5); if (short_irq < 0) printk("short: probe failed %i times, giving up\n", count);}void short_probing(int irq, void *dev_id, struct pt_regs *regs){ if (short_irq == 0) short_irq = irq; /* found */ if (short_irq != irq) short_irq = -irq; /* ambiguous */}void short_selfprobe(void){ int trials[] = {3, 5, 7, 9, 0}; int tried[] = {0, 0, 0, 0, 0}; int i, count = 0; /* * install the probing handler for all possible lines. Remember * the result (0 for success, or -EBUSY) in order to only free * what has been acquired */ for (i=0; trials[i]; i++) tried[i] = request_irq(trials[i], short_probing, SA_INTERRUPT, "short probe", NULL); do { short_irq = 0; /* none got, yet */ outb_p(0x10,short_base+2); /* enable */ outb_p(0x00,short_base); outb_p(0xFF,short_base); /* toggle the bit */ outb_p(0x00,short_base+2); /* disable */ udelay(5); /* give it some time */ /* the value has been set by the handler */ if (short_irq == 0) { /* none of them? */ printk(KERN_INFO "short: no irq reported by probe\n"); } /* * If more than one line has been activated, the result is * negative. We should service the interrupt (but the lpt port * doesn't need it) and loop over again. Do it at most 5 times */ } while (short_irq <=0 && count++ < 5); /* end of loop, uninstall the handler */ for (i=0; trials[i]; i++) if (tried[i] == 0) free_irq(trials[i], NULL); if (short_irq < 0) printk("short: probe failed %i times, giving up\n", count);}/* Two wrappers, to use non-page-aligned ioremap() on 2.0 *//* Remap a not (necessarily) aligned port region */void *short_remap(unsigned long phys_addr){ /* The code comes mainly from arch/any/mm/ioremap.c */ unsigned long offset, last_addr, size; last_addr = phys_addr + SHORT_NR_PORTS - 1; offset = phys_addr & ~PAGE_MASK; /* Adjust the begin and end to remap a full page */ phys_addr &= PAGE_MASK; size = PAGE_ALIGN(last_addr) - phys_addr; return ioremap(phys_addr, size) + offset;}/* Unmap a region obtained with short_remap */void short_unmap(void *virt_add){ iounmap((void *)((unsigned long)virt_add & PAGE_MASK));}/* Finally, init and cleanup */int short_init(void){ int result; /* * first, sort out the base/short_base ambiguity: we'd better * use short_base in the code, for clarity, but allow setting * just "base" at load time. Same for "irq". */ short_base = base; short_irq = irq; /* Set up owner pointers.*/ SET_MODULE_OWNER(&short_fops); SET_MODULE_OWNER(&short_i_fops); /* Get our needed resources. */ if (!use_mem) { result = check_region(short_base, SHORT_NR_PORTS); if (result) { printk(KERN_INFO "short: can't get I/O port address 0x%lx\n", short_base); return result; } request_region(short_base, SHORT_NR_PORTS, "short"); } else { result = check_mem_region(short_base, SHORT_NR_PORTS); if (result) { printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n", short_base); return result; } request_mem_region(short_base, SHORT_NR_PORTS, "short"); /* also, ioremap it */ short_phys = short_base; short_base = (unsigned long)short_remap(short_base); /* Hmm... we should check the return value */ } result = register_chrdev(major, "short", &short_fops); if (result < 0) { printk(KERN_INFO "short: can't get major number\n"); release_region(short_base,SHORT_NR_PORTS); return result; } if (major == 0) major = result; /* dynamic */ short_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */ short_head = short_tail = short_buffer; /* * Fill the short_task structure, used for the bottom half handler. * The cast is there to prevent warnings about the type of the * (unused) argument. */ short_task.routine = (void (*)(void *)) short_do_tasklet; short_task.data = NULL; /* unused */ /* * Now we deal with the interrupt: either kernel-based * autodetection, DIY detection or default number */ if (short_irq < 0 && probe == 1) short_kernelprobe(); if (short_irq < 0 && probe == 2) short_selfprobe(); if (short_irq < 0) /* not yet specified: force the default on */ switch(short_base) { case 0x378: short_irq = 7; break; case 0x278: short_irq = 2; break; case 0x3bc: short_irq = 5; break; } /* * If shared has been specified, installed the shared handler * instead of the normal one. Do it first, before a -EBUSY will * force short_irq to -1. */ if (short_irq >= 0 && share > 0) { result = request_irq(short_irq, short_sh_interrupt, SA_SHIRQ | SA_INTERRUPT,"short", short_sh_interrupt); if (result) { printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq); short_irq = -1; } else { /* actually enable it -- assume this *is* a parallel port */ outb(0x10,short_base+2); } return 0; /* the rest of the function only installs handlers */ } if (short_irq >= 0) { result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short", NULL); if (result) { printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq); short_irq = -1; } else { /* actually enable it -- assume this *is* a parallel port */ outb(0x10,short_base+2); } } /* * Ok, now change the interrupt handler if using top/bottom halves * has been requested */ if (short_irq >= 0 && (bh + tasklet) > 0) { free_irq(short_irq,NULL); result = request_irq(short_irq,#ifdef HAVE_TASKLETS tasklet ? short_tl_interrupt :#endif short_bh_interrupt, SA_INTERRUPT,"short-bh", NULL); if (result) { printk(KERN_INFO "short-bh: can't get assigned irq %i\n", short_irq); short_irq = -1; } } return 0;}void short_cleanup(void){ if (short_irq >= 0) { outb(0x0, short_base + 2); /* disable the interrupt */ if (!share) free_irq(short_irq, NULL); else free_irq(short_irq, short_sh_interrupt); } unregister_chrdev(major, "short"); if (use_mem) { short_unmap((void *)short_base); release_mem_region(short_base, SHORT_NR_PORTS); } else { release_region(short_base,SHORT_NR_PORTS); } if (short_buffer) free_page(short_buffer);}module_init(short_init);module_exit(short_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -