📄 short.c
字号:
/* * short.c -- Simple Hardware Operations and Raw Tests * short.c -- also a brief example of interrupt handling ("short int") * * untested (by now) the hardware r/w * * Tested with 2.0.18 (intel & alpha -- but no irq available for me) * This module won't work on the sparc, where there's no concept of I/O space * *********/#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#ifdef __sparc__# error "This module can't compile on the Sparc platform"#else#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/fs.h> /* everything... */#include <linux/errno.h> /* error codes */#include <linux/malloc.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/tqueue.h>#include <asm/io.h>#include <asm/segment.h>#include "sysdep.h"int short_major = 0;#ifdef __alpha__int short_base = 0x3bc; /* Hmm.... FIXME */#elseint short_base = 0x378; /* intel: default to lp0 */#endifint short_irq = -1;unsigned long short_buffer = 0;unsigned long volatile short_head;volatile unsigned long short_tail;struct wait_queue *short_queue;static int probe = 0; /* select at load time how to probe irq line */static int bh = 0; /* select at load time whether a bottom-half is used */static int share = 0; /* select at load time whether install a shared irq *//* * The devices with low minor numbers write/read burst of data to/from * specific I/O ports (by default the parallel ones). * * The device with 128 as minor number returns ascii strings telling * when interrupts have been received. Writing to the device toggles * 00/FF on the parallel data lines. If there is a loopback wire, this * generates interrupts. */int short_open (struct inode *inode, struct file *filp){ extern struct file_operations short_i_fops; MOD_INC_USE_COUNT; if (MINOR(inode->i_rdev) & 0x80) { filp->f_op = &short_i_fops; /* the interrupt-driven node */ } return 0;}void short_release (struct inode *inode, struct file *filp){ MOD_DEC_USE_COUNT;}/* first, the port-oriented device */enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING};read_write_t short_read (struct inode *inode, struct file *filp, char *buf, count_t count){ int retval = count; unsigned port = short_base + (MINOR(inode->i_rdev)&0x0f); int mode = (MINOR(inode->i_rdev)&0x70) >> 4; unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr; if (!kbuf) return -ENOMEM; ptr=kbuf; switch(mode) { case SHORT_STRING:#ifndef __alpha__ /* Alpha doesn'e export insb: fall through */ insb(port, ptr, count); break;#endif case SHORT_DEFAULT: while (count--) *(ptr++) = inb(port); break; case SHORT_PAUSE: while (count--) *(ptr++) = inb_p(port); break; default: /* no more modes defined by now */ retval = -EINVAL; break; } if (retval > 0) memcpy_tofs(buf, kbuf, retval); kfree(kbuf); return retval;}read_write_t short_write (struct inode *inode, struct file *filp, const char *buf, count_t count){ int retval = count; unsigned port = short_base + (MINOR(inode->i_rdev)&0x0f); int mode = (MINOR(inode->i_rdev)&0x70) >> 4; unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr; if (!kbuf) return -ENOMEM; memcpy_fromfs(kbuf, buf, count); ptr=kbuf; switch(mode) { case SHORT_PAUSE: while (count--) outb_p(*(ptr++), port); break; case SHORT_STRING:#ifndef __alpha__ /* Alpha doesn't export insb: fall through */ outsb(port, ptr, count); break;#endif case SHORT_DEFAULT: while (count--) outb(*(ptr++), port); break; default: /* no more modes defined by now */ retval = -EINVAL; break; } kfree(kbuf); return retval;}int short_select (struct inode *inode, struct file *filp, int mode, select_table *table){ return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */}struct file_operations short_fops = { NULL, /* short_lseek */ short_read, short_write, NULL, /* short_readdir */ short_select, NULL, /* short_ioctl */ NULL, /* short_mmap */ short_open, short_release, NULL, /* short_fsync */ NULL, /* short_fasync */ /* nothing more, fill with NULLs */};/* then, the interrupt-related device */read_write_t short_i_read (struct inode *inode, struct file *filp, char *buf, count_t count){ int count0; while (short_head == short_tail) { interruptible_sleep_on(&short_queue); if (current->signal & ~current->blocked) /* a signal arrived */ return -ERESTARTSYS; /* tell the fs layer to handle it */ /* else, loop */ } /* count0 is the number of readable data bytes */ count0 = short_head - short_tail; if (count0 < 0) /* wrapped */ count0 = short_buffer + PAGE_SIZE - short_tail; if (count0 < count) count = count0; memcpy_tofs(buf, (char *)short_tail, count); short_tail += count; if (short_tail == short_buffer + PAGE_SIZE) short_tail = short_buffer; return count;}read_write_t short_i_write (struct inode *inode, struct file *filp, const char *buf, count_t count){ int written = 0, odd = filp->f_pos & 1; unsigned port = short_base; /* output to the parallel data latch */ while (written < count) outb(0xff * ((++written + odd) & 1), port); filp->f_pos += count; return written;}struct file_operations short_i_fops = { NULL, /* short_i_lseek */ short_i_read, short_i_write, NULL, /* short_i_readdir */ NULL, /* short_i_select */ NULL, /* short_i_ioctl */ NULL, /* short_i_mmap */ short_open, short_release, NULL, /* short_i_fsync */ NULL, /* short_i_fasync */ /* nothing more, fill with NULLs */};/* * The interrupt handler has a different prototype whether it * is compiled with kernels older or newer than 1.3.70 */#if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)void short_interrupt(int irq, struct pt_regs *regs)#elsevoid short_interrupt(int irq, void *dev_id, struct pt_regs *regs)#endif{ struct timeval tv; do_gettimeofday(&tv); /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */ short_head += sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); if (short_head == short_buffer + PAGE_SIZE) short_head = short_buffer; /* wrap */ 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;struct timeval *tv_tail=tv_data;static struct tq_struct short_task; /* 0 by now, filled by init_module code */int short_bh_count = 0;void short_bottom_half(void *unused){ int savecount = short_bh_count; 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 */ short_head += sprintf((char *)short_head,"bh after %6i\n",savecount); if (short_head == short_buffer + PAGE_SIZE) short_head = short_buffer; /* wrap */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -