📄 short.c
字号:
/*
* short.c -- Simple Hardware Operations and Raw Tests
* short.c -- also a brief example of interrupt handling ("short int")
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
* $Id: short.c,v 1.22 2001/07/18 22:28:18 rubini Exp $
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#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/delay.h> /* udelay */
#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 "sysdep.h"
#define SHORT_NR_PORTS 8 /* use 8 ports by default */
/*
* all of the parameters have no "short_" prefix, to save typing when
* specifying them at load time
*/
static int major = 0; /* dynamic by default */
MODULE_PARM(major, "i");
static int use_mem = 0; /* default is I/O-mapped */
MODULE_PARM(use_mem, "i");
/* default is the first printer port on PC's. "short_base" is there too
because it's what we want to use in the code */
static unsigned long base = 0x378;
unsigned long short_base = 0;
MODULE_PARM(base, "l");
/* Since short_base is vremapped in case use_mem==1, remember the phys addr. */
unsigned long short_phys;
/* The interrupt line is undefined by default. "short_irq" is as above */
static int irq = -1;
volatile int short_irq = -1;
MODULE_PARM(irq, "i");
static int probe = 0; /* select at load time how to probe irq line */
MODULE_PARM(probe, "i");
static int bh = 0; /* select at load time whether a bottom-half is used */
MODULE_PARM(bh, "i");
static int tasklet = 0; /* select whether a tasklet is used */
MODULE_PARM(tasklet, "i");
static int share = 0; /* select at load time whether install a shared irq */
MODULE_PARM(share, "i");
MODULE_AUTHOR ("Alessandro Rubini");
unsigned long short_buffer = 0;
unsigned long volatile short_head;
volatile unsigned long short_tail;
DECLARE_WAIT_QUEUE_HEAD(short_queue);
/* Set up our tasklet if we're doing that. */
#ifdef HAVE_TASKLETS
void short_do_tasklet (unsigned long);
DECLARE_TASKLET (short_tasklet, short_do_tasklet, 0);
#endif
/*
* Atomicly increment an index into short_buffer
*/
static inline void short_incr_bp(volatile unsigned long *index, int delta)
{
unsigned long new = *index + delta;
barrier (); /* Don't optimize these two together */
*index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new;
}
/*
* 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;
}
#ifdef LINUX_20
void short_release (struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
}
#else
int short_release (struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
}
#endif
/* first, the port-oriented device */
enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING, SHORT_MEMORY};
ssize_t do_short_read (struct inode *inode, struct file *filp, char *buf,
size_t count, loff_t *f_pos)
{
int retval = count;
unsigned long address = 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;
if (use_mem)
mode = SHORT_MEMORY;
switch(mode) {
case SHORT_STRING:
insb(address, ptr, count);
rmb();
break;
case SHORT_DEFAULT:
while (count--) {
*(ptr++) = inb(address);
rmb();
}
break;
case SHORT_MEMORY:
while (count--) {
*(ptr++) = readb(address);
rmb();
}
break;
case SHORT_PAUSE:
while (count--) {
*(ptr++) = inb_p(address);
rmb();
}
break;
default: /* no more modes defined by now */
retval = -EINVAL;
break;
}
if ( (retval > 0) && copy_to_user(buf, kbuf, retval))
retval = -EFAULT;
kfree(kbuf);
return retval;
}
/*
* Version-specific methods for the fops structure.
*/
#ifdef LINUX_20
int short_read(struct inode *inode, struct file *filp, char *buf, int count)
{
return do_short_read(inode, filp, buf, count, &filp->f_pos);
}
#else
ssize_t short_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
return do_short_read(filp->f_dentry->d_inode, filp, buf, count, f_pos);
}
#endif
ssize_t do_short_write (struct inode *inode, struct file *filp, const char *buf,
size_t count, loff_t *f_pos)
{
int retval = count;
unsigned long address = 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;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
ptr=kbuf;
if (use_mem)
mode = SHORT_MEMORY;
switch(mode) {
case SHORT_PAUSE:
while (count--) {
outb_p(*(ptr++), address);
wmb();
}
break;
case SHORT_STRING:
outsb(address, ptr, count);
wmb();
break;
case SHORT_DEFAULT:
while (count--) {
outb(*(ptr++), address);
wmb();
}
break;
case SHORT_MEMORY:
while (count--) {
writeb(*(ptr++), address);
wmb();
}
break;
default: /* no more modes defined by now */
retval = -EINVAL;
break;
}
kfree(kbuf);
return retval;
}
#ifdef LINUX_20
int short_write(struct inode *inode, struct file *filp, const char *buf,
int count)
{
return do_short_write(inode, filp, buf, count, &filp->f_pos);
}
#else
ssize_t short_write(struct file *filp, const char *buf, size_t count,
loff_t *f_pos)
{
return do_short_write(filp->f_dentry->d_inode, filp, buf, count, f_pos);
}
#endif
#ifdef __USE_OLD_SELECT__
int short_poll (struct inode *inode, struct file *filp,
int mode, select_table *table)
{
return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */
}
#define poll select
#else /* Use poll */
unsigned int short_poll(struct file *filp, poll_table *wait)
{
return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}
#endif /* __USE_OLD_SELECT__ */
struct file_operations short_fops = {
read: short_read,
write: short_write,
poll: short_poll,
open: short_open,
release: short_release,
};
/* then, the interrupt-related device */
ssize_t short_i_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int count0;
while (short_head == short_tail) {
interruptible_sleep_on(&short_queue);
if (signal_pending (current)) /* 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;
if (copy_to_user(buf, (char *)short_tail, count))
return -EFAULT;
short_incr_bp (&short_tail, count);
return count;
}
ssize_t short_i_write (struct file *filp, const char *buf, size_t count,
loff_t *f_pos)
{
int written = 0, odd = *f_pos & 1;
unsigned long address = short_base; /* output to the parallel data latch */
if (use_mem) {
while (written < count)
writeb(0xff * ((++written + odd) & 1), address);
} else {
while (written < count)
outb(0xff * ((++written + odd) & 1), address);
}
*f_pos += count;
return written;
}
/*
* 2.0 wrappers.
*/
#ifdef LINUX_20
int short_i_read_20 (struct inode *inode, struct file *filp, char *buf,
int count)
{
return short_i_read (filp, buf, count, &filp->f_pos);
}
int short_i_write_20 (struct inode *inode, struct file *filp, const char *buf,
int count)
{
return short_i_write (filp, buf, count, &filp->f_pos);
}
#define short_i_read short_i_read_20
#define short_i_write short_i_write_20
#endif /* LINUX_20 */
struct file_operations short_i_fops = {
read: short_i_read,
write: short_i_write,
open: short_open,
release: short_release,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -