|
这个实例基本上和 http://www.groad.net/bbs/read.php?tid-1294.html中的中断实例一样,去被在于当调用请求 read() 时,因为没有中断发生(没有可读数据),调用进程会进入睡眠。
驱动程序测试代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#define BLOCKIO_DEV_NAME "blockiodev"
#define BLOCKIO_DEV_MAJOR 240
#define BLOCKIO_WRITE_ADDR 0x378
#define BLOCKIO_READ_ADDR 0x379
#define BLOCKIO_CTRL_ADDR 0x37A
#define BLOCKIO_IRQ 3
#define BLOCKIO_IRQ_ENABLE_MASK 0x10
#define BLOCKIO_BUFF_MAX 64
typedef struct {
unsigned long time;
}__attribute__ ((packed)) R_BLOCKIO_INFO;
R_BLOCKIO_INFO intbuffer[BLOCKIO_BUFF_MAX];
int intcount = 0;
DECLARE_WAIT_QUEUE_HEAD (WaitQueue_Read);
void blockio_clear (void)
{
int lp;
for (lp = 0; lp < BLOCKIO_BUFF_MAX; lp++)
{
intbuffer[lp].time = 0;
}
intcount = 0;
}
irqreturn_t blockio_interrupt (int irq, void *dev_id)
{
if (intcount < BLOCKIO_BUFF_MAX) {
intbuffer[intcount].time = get_jiffies_64();
intcount++;
}
wake_up_interruptible (&WaitQueue_Read); /*中断事件发生,唤醒进程*/
return (IRQ_HANDLED);
}
int blockio_open (struct inode *inode, struct file *filp)
{
if (!request_irq (BLOCKIO_IRQ, blockio_interrupt, IRQF_DISABLED, BLOCKIO_DEV_NAME, NULL)) {
outb(BLOCKIO_IRQ_ENABLE_MASK, BLOCKIO_CTRL_ADDR);
}
blockio_clear ();
return (0);
}
ssize_t blockio_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int readcount;
char *ptrdata;
int loop;
if (!intcount) {
if (!(filp->f_flags & O_NONBLOCK)) { /*应用程序不设置O_NONBLOCK非阻塞标志*/
interruptible_sleep_on (&WaitQueue_Read); /*进程睡眠*/
} else {
return (-EAGAIN);
}
}
readcount = count / sizeof (R_BLOCKIO_INFO);
if (readcount > intcount)
readcount = intcount; /*要求读的数量已超出实际可用的数量*/
ptrdata = (char *) &intbuffer[0];
for (loop = 0; loop < readcount * sizeof(R_BLOCKIO_INFO); loop++)
{
put_user (ptrdata[loop], (char *) &buf[loop]); /*每次以1字节传送*/
}
return (readcount * sizeof(R_BLOCKIO_INFO));
}
ssize_t blockio_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
unsigned char status;
int loop;
blockio_clear();
for (loop = 0; loop < count; loop++)
{
get_user (status, (char *)buf);
outb (status, BLOCKIO_WRITE_ADDR);
}
return (count);
}
int blockio_release (struct inode *inode, struct file *filp)
{
outb (0x00, BLOCKIO_CTRL_ADDR);
free_irq (BLOCKIO_IRQ, NULL);
return (0);
}
struct file_operations blockio_fops = {
.owner = THIS_MODULE,
.read = blockio_read,
.write = blockio_write,
.open = blockio_open,
.release = blockio_release,
};
int blockio_init (void)
{
int result;
result = register_chrdev (BLOCKIO_DEV_MAJOR, BLOCKIO_DEV_NAME, &blockio_fops);
if (result < 0)
return (result);
return (0);
}
void blockio_exit (void)
{
unregister_chrdev (BLOCKIO_DEV_MAJOR, BLOCKIO_DEV_NAME);
}
module_init (blockio_init);
module_exit (blockio_exit);
MODULE_LICENSE ("Dual BSD/GPL");
应用程序测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_FILENAME "/dev/blockiodev"
typedef struct {
unsigned long time;
}__attribute__ ((packed)) R_INT_INFO;
#define BLOCKIO_BUFF_MAX 64
int main()
{
R_INT_INFO intbuffer[BLOCKIO_BUFF_MAX];
int dev;
int intcount;
int loop;
char buff[128];
dev = open (DEVICE_FILENAME, O_RDWR);
if (dev > 0) {
printf ("start... input\n");
intcount = read (dev, (char *)&intbuffer[0], sizeof (R_INT_INFO));
printf ("input ok...\n");
sleep (1);
memset (intbuffer, 0, sizeof (intbuffer));
printf ("read interrupt times\n");
intcount = read (dev, (char *) intbuffer, sizeof (intbuffer)) / sizeof (R_INT_INFO);
for (loop = 0; loop < intcount; loop++)
{
printf ("index = %d time = %ld\n", loop, intbuffer[loop].time);
}
printf ("led flashing ...\n");
for (loop = 0; loop < 5; loop++)
{
buff[0] = 0xFF;
write (dev, buff, 1);
sleep (1);
buff[0] = 0x00;
write (dev, buff, 1);
sleep (1);
}
close (dev);
}
return (0);
} 说明:
在驱动程序中,当被应用程序调用 read() 读取数据时,如果 intcount = 0 ,则表示当前没可读数据;然后程序会检查 filp->f_flags 中是否设置了 O_NONBLOCK 值,也就是检查设备文件是否打开为非阻塞模式。若是以非阻塞模式打开,那么设备驱动程序返回 -EAGAIN 后立即结束函数;如果是阻塞模式,就会调用 interruptible_sleep_on() 函数,使调用进程进入睡眠状态。当有中断发生时,在中断处理函数里会调用 wake_up_interruptible() 函数唤醒等待队列. |
|