有很多LINUX/UNIX学习资料
UPDATE:
Linux Device Driver Template/Skeleton with Interrupt Handler and Device Read Blocking (Kernel Module Example)
For a template with memory mapping see:
Linux Device Driver Template/Skeleton with memory mapping of kernel memory to user-space (mmap)
This is a Linux Device Driver Template/Skeleton including an interrupt handler for the parallel port. The driver creates a device entry in /dev/, which can be used to communicate with the kernel module (read, write, ioctl).
Compile everything with the "Makefile" and before you load the kernel module with Kernel 2.4:
# insmod ./skeleton.o
Kernel 2.6:
# insmod ./skeleton.ko
you must create the device entry in /dev with # mknod -m 666 /dev/skeleton c 32 0
When the module is loaded, it creates an interrupt; you see some messages in the kernel log file:
initializing module
Generating interrupt now on all output pins (intr/ACK = pin 10)
>>> PARALLEL PORT INT HANDLED: interruptcount=1
Interrupt generated. You should see the handler-message
In order to see the interrupt, you need to make a parallel port "null-modem". Connect any output pin (pin 2 - 9) with the IRQ pin (pin 10). You see the "outb_p"'s in skeleton_init_module to generate the pulse signal on the output pins.
After loading the module, start the userspace program "user". This program opens the device (/dev/skeleton), writes data to it and reads the data back. The kernel module is programmed to block the read access until an interrupt occurs. After starting the userspace program, use the to generate another interrupt and to unblock the read function. You will these messages:
# ./user
String 'Skeleton Kernel Module Test' written to /dev/skeleton
String 'Skeleton Kernel Module Test' read from /dev/skeleton
IOCTL test: written: '55555555' - received: '55555555'
After starting "user", you will see "String 'Skeleton Kernel Module Test' written to /dev/skeleton" and the program waits. Generate the interrupt with "set" and "reset", the module unblocks the read function and the additional messages appear.
Furthermore the userspace program does some basic IOCTL access - you can use this to send commands to the module. i.e. to start sampling data with an ADC over the parallel port. If you want to do the interrupt handling in RTAI hard real time, have a look at .
Another way to test the driver:
Write some data to the module: # cat > /dev/skeleton
this is a pen.
[press CTRL-D]
#
Read the data back: # cat < /dev/skeleton
this is a pen.
#
The device driver "skeleton.c": // Linux Device Driver Template/Skeleton
// Kernel Module
#include
#include
#include
#include
#include
#include
#include
#include
#define SKELETON_MAJOR 32
#define SKELETON_NAME "skeleton"
#define BASEPORT 0x378
#define PARALLEL_PORT_INTERRUPT 7
#define CASE1 1
#define CASE2 2
static unsigned int counter = 0;
static char string [128];
static int data;
static int interruptcount = 0;
DECLARE_WAIT_QUEUE_HEAD(skeleton_wait);
static int data_not_ready = 1;
// open function - called when the "file" /dev/skeleton is opened in userspace
static int skeleton_open (struct inode *inode, struct file *file) {
printk("skeleton_open\n");
// we could do some checking on the flags supplied by "open"
// i.e. O_NONBLOCK
// -> set some flag to disable interruptible_sleep_on in skeleton_read
return 0;
}
// close function - called when the "file" /dev/skeleton is closed in userspace
static int skeleton_release (struct inode *inode, struct file *file) {
printk("skeleton_release\n");
return 0;
}
// read function called when from /dev/skeleton is read
static ssize_t skeleton_read (struct file *file, char *buf,
size_t count, loff_t *ppos) {
int len, err;
// check if we have data - if not, sleep
// wake up in interrupt_handler
while (data_not_ready) {
interruptible_sleep_on(&skeleton_wait);
}
data_not_ready = 1;
if( counter <= 0 )
return 0;
err = copy_to_user(buf,string,counter);
if (err != 0)
return -EFAULT;
len = counter;
counter = 0;
return len;
}
// write function called when to /dev/skeleton is written
static ssize_t skeleton_write (struct file *file, const char *buf,
size_t count, loff_t *ppos) {
int err;
err = copy_from_user(string,buf,count);
if (err != 0)
return -EFAULT;
counter += count;
return count;
}
// ioctl - I/O control
static int skeleton_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) {
int retval = 0;
switch ( cmd ) {
case CASE1:/* for writing data to arg */
if (copy_from_user(&data, (int *)arg, sizeof(int)))
return -EFAULT;
break;
case CASE2:/* for reading data from arg */
if (copy_to_user((int *)arg, &data, sizeof(int)))
return -EFAULT;
break;
default:
retval = -EINVAL;
}
return retval;
}
// interrupt handler
static int interrupt_handler(void)
{
// do stuff
interruptcount++;
printk(">>> PARALLEL PORT INT HANDLED: interruptcount=%d\n", interruptcount);
// wake up (unblock) for reading data from userspace
// and ignore first interrupt generated in module init
if (interruptcount > 1) {
data_not_ready = 0;
wake_up_interruptible(&skeleton_wait);
}
return IRQ_HANDLED;
}
// define which file operations are supported
struct file_operations skeleton_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = skeleton_read,
.write = skeleton_write,
.readdir = NULL,
.poll = NULL,
.ioctl = skeleton_ioctl,
.mmap = NULL,
.open = skeleton_open,
.flush = NULL,
.release = skeleton_release,
.fsync = NULL,
.fasync = NULL,
.lock = NULL,
.readv = NULL,
.writev = NULL,
};
// initialize module (and interrupt)
static int __init skeleton_init_module (void) {
int i;
int ret;
printk("initializing module\n");
i = register_chrdev (SKELETON_MAJOR, SKELETON_NAME, &skeleton_fops);
if (i != 0) return - EIO;
// INIT AND GENERATE INTERRUPT
ret = request_irq(PARALLEL_PORT_INTERRUPT, &interrupt_handler,
SA_INTERRUPT, "parallelport", NULL);
enable_irq(7);
//set parallel port to interrupt mode; pins are output
outb_p(0x10, BASEPORT + 2);
printk("Generating interrupt now on all output pins (intr/ACK = pin 10)\n");
//generate interrupt
outb_p(0, BASEPORT);
outb_p(255, BASEPORT);
outb_p(0, BASEPORT);
printk("Interrupt generated. You should see the handler-message\n");
return 0;
}
// close and cleanup module
static void __exit skeleton_cleanup_module (void) {
printk("cleaning up module\n");
unregister_chrdev (SKELETON_MAJOR, SKELETON_NAME);
disable_irq(7);
free_irq(7, NULL);
}
module_init(skeleton_init_module);
module_exit(skeleton_cleanup_module);
MODULE_AUTHOR("");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Linux Device Driver Template/Skeleton with Interrupt Handler");
Userspace test program "user.c"
// Linux Device Driver Template/Skeleton
// Userspace test program
#include
#include
#include
#include
#include
#include
#define CASE1 1
#define CASE2 2
main() {
int fd, len, wlen;
char string[] = "Skeleton Kernel Module Test";
char receive[128];
int data, rdata;
fd = open("/dev/skeleton", O_RDWR);
if( fd == -1) {
printf("open error...\n");
exit(0);
}
wlen = strlen(string) + 1;
len = write(fd, string, wlen);
if( len == -1 ) {
printf("write error...\n");
exit(1);
}
printf("String '%s' written to /dev/skeleton\n", string);
len = read(fd, receive, 128);
if( len == -1 ) {
printf("read error...\n");
exit(1);
}
printf("String '%s' read from /dev/skeleton\n", receive);
data = 0x55555555;
ioctl(fd, CASE1, &data);
ioctl(fd, CASE2, &rdata);
printf("IOCTL test: written: '%x' - received: '%x'\n", data, rdata);
close(fd);
}
Makefile
UNAME := $(shell uname -r)
KERNEL26 := 2.6
KERNELVERSION := $(findstring $(KERNEL26),$(UNAME))
all:: user
ifeq ($(KERNELVERSION),2.6)
obj-m := skeleton.o
INCLUDE := -I/usr/include/asm/mach-default/
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all::
$(MAKE) -C $(KDIR) $(INCLUDE) SUBDIRS=$(PWD) modules
else
TARGET := skeleton
INCLUDE := -I/lib/modules/`uname -r`/build/include -I/usr/include/asm/mach-default/
CFLAGS := -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX
CC := gcc
all:: ${TARGET}.o
${TARGET}.o: ${TARGET}.c
$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c
endif
user: user.c
gcc -o $@ $<