刚刚电脑突然蓝屏死机了,,,第一版快要写完了突然就搞事情,还让不让我好好跨年了。今天是2018-12-31 21:30,祝各位新年快乐,事业有成,两位指导导师身体健康。2019年好好做项目,实验室的同志们一起努力。
编译系统 :ubuntu 16.04
内 核 :linux-2.6.22.6
硬件平台 :jz2240
交叉编译器:arm-linux-gcc 3.4.5
先说一下什么是异步通知机制:一旦设备符合条件,就会主动通知用户程序,这样就可以让用户程序省心,可以专注做自己的事情。当有异步通知时,再去处理对应的程序。这点和单片机上的中断很类似。但是这是软件层次的中断,一般通过信号来传递的,也称之为“信号驱动的异步IO”。
话不多说,直接上代码,用户程序代码(因为是用户测试驱动,所有用户程序看起来很简单,实际项目中主要精力都花在用户程序中)如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int g_fd;//文件描述符
void my_handler(int sign_num)
{
int readval;//read函数读返回值
char keyval = 0;//按键值
static int count=0;//记录发生多少次按键
readval = read(g_fd,&keyval,sizeof(char));//没有中断时,休眠
if(readval < 0)
{
printf("no data to read\n");
exit(-1);
}
printf("keyval = 0x%x count = %d\n",keyval,++count);
}
int main(int argc,int **argv)
{
int get_flag;//fcntl读取的文件状态标志
//打开设备文件,非阻塞方式
g_fd = open("/dev/buttons",O_RDONLY|O_NONBLOCK);
if(g_fd < 0)
{
perror("open /dev/buttons");
return -1;
}
signal(SIGIO,my_handler);//接收到信号
fcntl(g_fd,F_SETOWN,getpid());//通过命令将设置设备文件的拥有者为本进程
get_flag = fcntl(g_fd,F_GETFL);
fcntl(g_fd,F_SETFL,get_flag|FASYNC);//通过命令将设置设备文件支持FASYNC,即异步机制
for(;;)
{
sleep(1000*10);//休眠10s,这里模拟实际代码
}
close(g_fd);
return 0;
}
这里要简单分析一下:
signal(SIGIO,my_handler); //接收到信号,当驱动程序主动发送信号到用户程序中,用户程序就会自动捕获SIGIO类型的信号,然后转到my_handler函数中去执行对应的代码。
fcntl(g_fd,F_SETOWN,getpid());//通过命令将设置设备文件的拥有者为本进程
get_flag = fcntl(g_fd,F_GETFL);//获取当前设备文件标志
fcntl(g_fd,F_SETFL,get_flag|FASYNC);//通过命令将设置设备文件支持FASYNC,即异步机制
接下来就用for(;;) 内休眠模拟实际代码,这里就是实际需要处理其他的事情。
驱动代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 这是一个按键驱动
* 如果没有按键按下,进行read就进行休眠
* 为什么要休眠,因为应用程序会一直在读或者等到按键被按下来,
* 如果说一直没有按下按键,那么就需要让用户空间进行休眠,不然会一直处于读阻塞
*/
//创建一个类
static struct class *g_buttons_class;
//在类下面创建一个设备
static struct class_device *g_my_buttons_classdevice;
struct buttondesc
{
unsigned int key_gpio; //引脚
unsigned int irq ; //中断号
unsigned int flag; //触发条件
const char *name; //名字
char keyval;//自定义键值
};
static struct buttondesc g_buttons[4] =
{
{S3C2410_GPF0, IRQ_EINT0, IRQT_FALLING, "S2",0x11},
{S3C2410_GPF2, IRQ_EINT2, IRQT_FALLING, "S3",0x12},
{S3C2410_GPG3, IRQ_EINT11, IRQT_FALLING, "S4",0x13},
{S3C2410_GPG11, IRQ_EINT19, IRQT_FALLING, "S5",0x14},
};
static char g_keyval;//用于返回给用户空间
/* ------------------------------------------------------------------------- */
/* ################## 休眠相关变量 ################## */
#define SLEEP 1
#define NO_SLEEP 0
static DECLARE_WAIT_QUEUE_HEAD(g_buttons_waitq);
static char g_ev_press = SLEEP;//用于标志中断休眠
/* ------------------------------------------------------------------------- */
/* ################## 原子操作相关变量 ################## */
static atomic_t g_atomic = ATOMIC_INIT(1);//定义一个原子,并且初始化为1
/* ------------------------------------------------------------------------- */
/* ################## 异步相关变量 ################## */
struct fasync_struct *fasync;
/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
//实际的中断处理函数
static irq_handler_t buttons_handler(int irq, void *dev_id)
{
struct buttondesc *temp_desc = (struct buttondesc *)dev_id;//根据传递参数可以获得对应中断号数组
unsigned int temp_key = s3c2410_gpio_getpin(temp_desc->key_gpio);//读取gpio引脚状态
if(!temp_key)//如果有按键按下就是0
g_keyval = 0x80 | temp_desc->keyval;
else
g_keyval = temp_desc->keyval;
//访问全局变量这里要进行原子操作
if(!atomic_dec_and_test(&g_atomic))//测试其是否为0,为 0 则返回 true,否则返回 false
{
//已经打开,不能再次打开
atomic_inc(&g_atomic);
return -1;
}
g_ev_press = NO_SLEEP;//唤醒休眠
atomic_inc(&g_atomic);//恢复原子值
wake_up_interruptible(&g_buttons_waitq);//唤醒挂在 g_buttons_waitq 上的进程
kill_fasync(&fasync,SIGIO,POLL_IN);//发送异步信号 POLL_IN可读
return 0;
}
//驱动读函数
static ssize_t buttons_read(struct file *fp, char __user *buf, size_t size, loff_t *fops)
{
if(g_ev_press == SLEEP)//处于睡眠状态,此时没有数据
{
if(fp->f_flags & O_NONBLOCK)//处于阻塞读取
{
return -1;
}
else
{
wait_event_interruptible(g_buttons_waitq, (g_ev_press == NO_SLEEP));//当g_ev_press == NO_SLEEP条件符合是 会被唤醒,继续执行
}
}
//执行到这里说明被中断唤醒了
if(copy_to_user(buf,&g_keyval, 1))//返回到用户空间
printk("copy_to_user not complete\n");
//访问全局变量这里要进行原子操作
if(!atomic_dec_and_test(&g_atomic))//测试其是否为0,为 0 则返回 true,否则返回 false
{
//已经打开,不能再次打开
atomic_inc(&g_atomic);
return -1;
}
g_ev_press = SLEEP;//重新进行休眠
atomic_inc(&g_atomic);//恢复原子值
return 0;
}
//驱动poll,供应用程序selec和poll使用
static unsigned int buttons_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(filp, &g_buttons_waitq, wait);//并不是表示阻塞,而是代表g_buttons_waitq唤醒可唤醒select
if (g_ev_press == NO_SLEEP)/* 可读 */
mask |= POLLIN | POLLRDNORM;//POLLIN表示可以无阻塞读
return mask;
}
static int button_fasync(int fd, struct file *filp, int on)
{
printk(KERN_INFO "--------- button_fasync --------- \n");
fasync_helper(fd,filp,on,&fasync);
return 0;
}
//驱动打开函数
static int buttons_open(struct inode *inodep, struct file *fp)
{
int i;
//注册中断
for(i = 0; i < sizeof(g_buttons)/sizeof(struct buttondesc); i++)
request_irq(g_buttons[i].irq,buttons_handler,g_buttons[i].flag,g_buttons[i].name,(void *)(&g_buttons[i]));
//访问全局变量这里要进行原子操作
if(!atomic_dec_and_test(&g_atomic))//测试其是否为0,为 0 则返回 true,否则返回 false
{
//已经打开,不能再次打开
atomic_inc(&g_atomic);
return -1;
}
g_ev_press = SLEEP;//进行休眠
atomic_inc(&g_atomic);//恢复原子值
printk(KERN_INFO "open buttons success\n");
return 0;
}
//关闭
static int buttons_close(struct inode *inodep, struct file *fp)
{
int i;
for(i = 0; i < sizeof(g_buttons)/sizeof(struct buttondesc); i++)
free_irq(g_buttons[i].irq,&g_buttons[i]);
button_fasync(-1,fp,0);//释放异步,将文件从异步通知列表中取消
return 0;
}
//操作函数集
static struct file_operations g_buttons_op =
{
.owner = THIS_MODULE,
.open = buttons_open,
.read = buttons_read,
.release = buttons_close,
.poll = buttons_poll,
.fasync = button_fasync,
};
int g_buttons_major;
static int __init buttons_init(void)
{
g_buttons_major = register_chrdev(0, "my_buttons", &g_buttons_op); //注册设备
g_buttons_class = class_create(THIS_MODULE, "HBUT_class"); //创建类
g_my_buttons_classdevice = class_device_create(g_buttons_class, NULL, MKDEV(g_buttons_major, 0), NULL, "buttons"); // /dev/led %d
return 0;
}
static void __exit buttons_exit(void)
{
unregister_chrdev(g_buttons_major,"my_buttons");
class_device_destroy(g_buttons_class,MKDEV(g_buttons_major, 0));
class_destroy(g_buttons_class);
}
MODULE_AUTHOR("大白菜");
MODULE_DESCRIPTION("buttons drive with sleep and interrupt");
MODULE_LICENSE("GPL");
module_init(buttons_init);
module_exit(buttons_exit);
这里简单分析一下驱动中代码
//当用户空间的设备文件状态发生变化就会调用驱动程序中的该函数button_fasync
static int button_fasync(int fd, struct file *filp, int on)
{
printk(KERN_INFO "--------- button_fasync --------- \n");
fasync_helper(fd,filp,on,&fasync);
return 0;
}
static irq_handler_t buttons_handler(int irq, void *dev_id)
{
、、、
、、、
、、、
kill_fasync(&fasync,SIGIO,POLL_IN);//发送异步信号 POLL_IN可读
、、、
、、、
、、、
return 0;
}
在可以获取资源时,在中断处理函数中,会调用kill_fasync(&fasync,SIGIO,POLL_IN)函数给用户程序发送SIGIO信号,其中POLL_IN代表可读,POLL_OUT表示可写。此时的用户程序无论在干嘛都会收到这个信号,然后转去处理该信号。