• 使用休眠-唤醒、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

    • image.png
    • 驱动编程

    使用异步通知时,驱动程序的核心有 2:
    ① 提供对应的 drv_fasync 函数;
    ② 并在合适的时机发信号。

    1. struct fasync_struct *button_fasync;
    2. static int gpio_key_drv_fasync(int fd, struct file *file, int on)
    3. {
    4. if (fasync_helper(fd, file, on, &button_fasync) >= 0)
    5. return 0;
    6. else
    7. return -EIO;
    8. }
    9. /* 在中断处理函数中,使用kill_fasync发送信号 */
    10. static irqreturn_t gpio_key_isr(int irq, void *dev_id)
    11. {
    12. struct gpio_key *gpio_key = dev_id;
    13. int val;
    14. int key;
    15. val = gpiod_get_value(gpio_key->gpiod);
    16. printk("key %d %d\n", gpio_key->gpio, val);
    17. key = (gpio_key->gpio << 8) | val;
    18. put_key(key);
    19. /* 唤醒等待队列 */
    20. wake_up_interruptible(&gpio_key_wait);
    21. /* 有数据被唤醒后发送信号 */
    22. kill_fasync(&button_fasync, SIGIO, POLL_IN);
    23. return IRQ_HANDLED;
    24. }
    • 应用编程 ```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) {

      1. printf("Usage: %s <dev>\n", argv[0]);
      2. return -1;

      }

      / 注册信号函数 / signal(SIGIO, sig_func);

      / 2. 打开文件 / fd = open(argv[1], O_RDWR); if (fd == -1) {

      1. printf("can not open file %s\n", argv[1]);
      2. return -1;

      }

      fcntl(fd, F_SETOWN, getpid()); / 把进程号告诉驱动 / / 使能驱动的 FASYNC 功能 / flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC);

      while (1) {

      1. printf("www.100ask.net \n");
      2. sleep(2);

      }

      close(fd);

      return 0; } ```