2005-11-18 15:13
ling
测试中断,但是申请空的i/o资源却总是不成功,看看哪里出错?!! 先谢了!
程序源代码如下,改至linux设备驱动程序的示例 short,就是只保留它的中断部分,
但每次加载模块,都说资源忙,申请不成功. 而且对该资源好像有破 huai--------???????why???
code:
/*
* Simple Hardware Operations and Raw Tests
* simpleirq.c -- a brief example of interrupt handling ("simple_irq")
*/
#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/slab.h> /*<linux/malloc.h>*/
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/tqueue.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define SIMPLE_NR_PORTS 8 /* use 8 ports by default */
/*
* all of the parameters have no "simple_" prefix, to save typing when
* specifying them at load time
*/
static int major = 0; /* dynamic by default */
MODULE_PARM(major, "i");
/* default is the first printer port on PC's. "simple_base" is there too
because it's what we want to use in the code */
static unsigned long base = 0x378;
unsigned long simple_base = 0;
MODULE_PARM(base, "l");
/* Since simple_base is vremapped in case use_mem==1, remember the phys addr. */
unsigned long simple_phys;
/* The interrupt line is undefined by default. "simple_irq" is as above */
static int irq = -1;
volatile int simple_irq = -1;
MODULE_PARM(irq, "i");
unsigned long simple_buffer = 0;
unsigned long volatile simple_head;
volatile unsigned long simple_tail;
DECLARE_WAIT_QUEUE_HEAD(simple_queue);
/*
* Atomicly increment an index into simple_buffer
*/
static inline void simple_incr_bp(volatile unsigned long *index, int delta)
{
unsigned long newone = *index + delta;
barrier (); /* Don't optimize these two together */
*index = ( newone >= (simple_buffer + PAGE_SIZE)) ? simple_buffer : newone;
}
/*
* 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 simple_open (struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return 0;
}
int simple_release (struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
}
/* then, the interrupt-related device */
ssize_t simple_i_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int count0;
while (simple_head == simple_tail)
{
interruptible_sleep_on(&simple_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 = simple_head - simple_tail;
if (count0 < 0) /* wrapped */
count0 = simple_buffer + PAGE_SIZE - simple_tail;
if (count0 < count)
count = count0;
if (copy_to_user(buf, (char *)simple_tail, count))
return -EFAULT;
simple_incr_bp (&simple_tail, count);
return count;
}
ssize_t simple_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 = simple_base; /* output to the parallel data latch */
while (written < count)
outb(0xff * ((++written + odd) & 1), address); //send interrupt
*f_pos += count;
return written;
}
struct file_operations simple_i_fops = {
read: simple_i_read,
write: simple_i_write,
open: simple_open,
release: simple_release,
};
void simple_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 *)simple_head,"%08u.%06u\n",
(int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
simple_incr_bp(&simple_head, written);
wake_up_interruptible(&simple_queue); /* awake any reading process */
}
/* Finally, init and cleanup */
int simple_init(void)
{
int result;
/*
* first, sort out the base/simple_base ambiguity: we'd better
* use simple_base in the code, for clarity, but allow setting
* just "base" at load time. Same for "irq".
*/
simple_base = base;
simple_irq = irq;
/* Set up owner pointers.*/
SET_MODULE_OWNER(&simple_i_fops);
/* Get our needed resources. */
result = check_region(simple_base, SIMPLE_NR_PORTS);
if (result)
{
printk(KERN_INFO "simple: can't get I/O port address 0x%lx\n",
simple_base);
return result;
}
request_region(simple_base, SIMPLE_NR_PORTS, "simpleirq");
result = register_chrdev(major, "simpleirq", &simple_i_fops);
if (result < 0)
{
printk(KERN_INFO "simple: can't get major number\n");
release_region(simple_base,SIMPLE_NR_PORTS);
return result;
}
if (major == 0)
major = result; /* dynamic */
simple_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */
simple_head = simple_tail = simple_buffer;
/*
* Now we deal with the interrupt: either kernel-based
* autodetection, DIY detection or default number
*/
if (simple_irq < 0) /* not yet specified: force the default on */
switch(simple_base)
{
case 0x378: simple_irq = 7; break;
case 0x278: simple_irq = 2; break;
case 0x3bc: simple_irq = 5; break;
}
if (simple_irq >= 0)
{
result = request_irq(simple_irq, simple_interrupt,
SA_INTERRUPT, "simpleirq", NULL);
if (result)
{
printk(KERN_INFO "simple: can't get assigned irq %i\n",
simple_irq);
simple_irq = -1;
}
else { /* actually enable it -- assume this *is* a parallel port */
outb(0x10,simple_base+2);
}
}
}
void simple_cleanup(void)
{
if (simple_irq >= 0) {
outb(0x0, simple_base + 2); /* disable the interrupt */
free_irq(simple_irq, NULL);
}
unregister_chrdev(major, "simpleirq");
release_region(simple_base,SIMPLE_NR_PORTS);
if (simple_buffer)
free_page(simple_buffer);
}
module_init(simple_init);
module_exit(simple_cleanup);
$gcc -I/usr/src/linux-2.4.18-3/include -D__KERNEL__ -DMODULE -DLINUX -c simpleirq.c
$insmod simpleirq.o
Warning: loading simpleirq.o will taint the kernel: no license
simpleirq.o: init_module: Device or resource busy
Hint: insmod errors can be caused by incorrect module parameters, including invalid IO or IRQ parameters
$cat /var/log/messages
.....
can't get I/O port address 0x378
$cat /proc/ioports
Segmentation fault
$cat /proc/interrupts
Segmentation fault
$cat /proc/devices
Segmentation fault