- 使用休眠-唤醒、POLL 机制时,都需要休眠等待某个事件发生时,它们的差别在于后者可以指定休眠的时长。
- 如果 APP 不想休眠怎么办?驱动程序有数据时主动通知 APP,APP 收到信号后执行信息处理函数。
- 驱动程序怎么通知 APP:发信号
① 谁发:驱动程序发
② 发什么:信号
③ 发什么信号:SIGIO
④ 怎么发:内核里提供有函数 (kill_fasync)
⑤ 发给谁:APP,APP 要把自己告诉驱动
⑥ APP 收到后做什么:执行信号处理函数
⑦ 信号处理函数和信号,之间怎么挂钩:APP 注册信号处理函数
- 就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要跟 SIGIO 挂钩。可以通过一个 signal 函数来“给某个信号注册处理函数”
- APP要做什么事?
① 内核里有那么多驱动,你想让哪一个驱动给你发 SIGIO 信号?
APP 要打开驱动程序的设备节点。
② 驱动程序怎么知道要发信号给你而不是别人?
APP 要把自己的进程 ID 告诉驱动程序 (fcntl)。
③ APP 有时候想收到信号,有时候又不想收到信号:
应该可以把 APP 的意愿告诉驱动。
- 驱动程序要做什么?
① APP 设置进程 ID 时,驱动程序要记录下进程 ID;
② APP 还要使能驱动程序的异步通知功能,驱动中有对应的函数:
APP 打开驱动程序时,内核会创建对应的 file 结构体,file 中有 f_flags;
f_flags 中有一个 FASYNC 位,它被设置为 1 时表示使能异步通知功能。
当 f_flags 中的 FASYNC 位发生变化时,驱动程序的 fasync 函数被调用。
③ 发生中断时,有数据时,驱动程序调用内核辅助函数发信号。
这个辅助函数名为 kill_fasync
- 驱动编程
使用异步通知时,驱动程序的核心有 2:
① 提供对应的 drv_fasync 函数;
② 并在合适的时机发信号。
struct fasync_struct *button_fasync;
static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)
return 0;
else
return -EIO;
}
/* 在中断处理函数中,使用kill_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);
printk("key %d %d\n", gpio_key->gpio, val);
key = (gpio_key->gpio << 8) | val;
put_key(key);
/* 唤醒等待队列 */
wake_up_interruptible(&gpio_key_wait);
/* 有数据被唤醒后发送信号 */
kill_fasync(&button_fasync, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
- 应用编程 ```c static int fd; static void sig_func(int sig) { int val; read(fd, &val, 4); printf(“get button : 0x%x\n”, val); }
/*
./button_test /dev/100ask_button0 / int main(int argc, char **argv) { int val; struct pollfd fds[1]; int timeout_ms = 5000; int ret; int flags;
/ 1. 判断参数 / if (argc != 2) {
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/ 注册信号函数 / signal(SIGIO, sig_func);
/ 2. 打开文件 / fd = open(argv[1], O_RDWR); if (fd == -1) {
printf("can not open file %s\n", argv[1]);
return -1;
}
fcntl(fd, F_SETOWN, getpid()); / 把进程号告诉驱动 / / 使能驱动的 FASYNC 功能 / flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC);
while (1) {
printf("www.100ask.net \n");
sleep(2);
}
close(fd);
return 0; } ```