linux应用与驱动之间的通讯--驱动篇
应用与驱动之间调用关系
首先我们要明白,应用程序是不能直接操作内核的,由于写应用程序的人水平参差不齐,又或是动机不良,直接允许应用程序操作内核是件危险的事。所以,驱动只要提供标准接口,供应用程序调用即可。
如应用调用read,write,open,ioctl, 驱动则提供对于的drv_read,drv_write,drv_ioctl。
阻塞与非阻塞
对于非阻塞,驱动程序在应用程序调用read时,提供驱动程序的drv_read,所以主要判断传入的参数,在drv_read函数中,判断file->flag是否为O_NONBLOCK即可
对于阻塞,只要传入的flag不为O_NONBLOCK即可,当无数据时,程序会进入休眠,直到被唤醒。
① APP调用read等函数试图读取数据,比如读取按键;
② APP进入内核态,也就是调用驱动中的对应函数,发现有数据则复制到用户空间并马上返回;
③ 如果APP在内核态,也就是在驱动程序中发现没有数据,则APP休眠;
④ 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒APP;
⑤ APP继续运行它的内核态代码,也就是驱动程序中的函数,复制数据到用户空间并马上返回。
函数 说明
1.wait_event_interruptible(wq, condition) 休眠,直到condition为真;休眠期间是可被打断的,可以被信号打断
2.wait_event(wq, condition) 休眠,直到condition为真;退出的唯一条件是condition为真,信号也不好使
3.wait_event_interruptible_timeout(wq, condition, timeout) 休眠,直到condition为真或超时;休眠期间是可被打断的,可以被信号打断
4.wait_event_timeout(wq, condition, timeout) 休眠,直到condition为真;退出的唯一条件是condition为真,信号也不好使
① wq:waitqueue,等待队列
休眠时除了把程序状态改为非RUNNING之外,还要把进程/进程放入wq中,以后中断服务程序要从wq中把它取出来唤醒。
② condition
这可以是一个变量,也可以是任何表达式。表示“一直等待,直到condition为真”。
//如果没有值,且是以非阻塞打开的,立即返回
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait); //声明
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int err;
int key;
if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());//调用并不会马上休眠,先判断条件
key = get_key();
err = copy_to_user(buf, &key, 4);
return 4;
}
//中断
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
struct gpio_key *gpio_key = dev_id;
int val;
val = gpiod_get_value(gpio_key->gpiod);
g_key = (gpio_key->gpio << 8) | val;
wake_up_interruptible(&gpio_key_wait);//唤醒
return IRQ_HANDLED;
}
POLL
① APP不知道驱动程序中是否有数据,可以先调用poll函数查询一下,poll函数可以传入超时时间;
② APP进入内核态,调用到驱动程序的poll函数,如果有数据的话立刻返回;
③ 如果发现没有数据时就休眠一段时间;
④ 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒APP;
⑤ 当超时时间到了之后,内核也会唤醒APP;
⑥ APP根据poll函数的返回值就可以知道是否有数据,如果有数据就调用read得到数据
在file_operations结构体中,定义poll函数
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait); //声明
unsigned int gpio_key_drv_poll (struct file *fp, struct poll_table_struct *wait)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp ,&gpio_key_wait, wait);
return is_key_buf_empty() ?0 :POLLIN| POLLRDNORM;
}
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.read = gpio_key_drv_read,
.poll = gpio_key_drv_poll,
};
异步通知
在file_operations结构体中定义fasync函数
static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)//fasync_helper函数会分配、构造一个fasync_struct结构体
return 0;
else
return -EIO;
}
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.read = gpio_key_drv_read,
.fasync = gpio_key_drv_fasync,
};
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
struct gpio_key *gpio_key = dev_id;
int val;
int key;
val = gpiod_get_value(gpio_key->gpiod);
kill_fasync(&button_fasync, SIGIO, POLL_IN);//取出PID,发送信号
return IRQ_HANDLED;
}
驱动程序程序“只提供功能,不提供策略”。就是说驱动程序可以提供休眠唤醒、查询等等各种方式,,驱动程序只提供这些能力,怎么用由APP决定。