• RK3568驱动指南|第五期-中断-第45章 自定义工作队列实验


    瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


    【公众号】迅为电子

    【粉丝群】824412014(加群获取驱动文档+例程)

    【视频观看】嵌入式学习之Linux驱动(第五期_中断_全新升级)_基于RK3568

    【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


    第45章 自定义工作队列实验

    在上一章节中对工作队列以及共享工作队列知识进行了学习,并使用共享队列进行了实验。共享队列是由内核管理的全局工作队列,自定义工作队列是由内核或驱动程序创建的特定工作队列,用于处理特定的任务。下面就让我们一起来进行自定义工作队列的学习吧。

    45.1 工作队列相关结构体

    在Linux内核中,结构体struct work_struct描述的是要延迟执行的工作项,定义在include/linux/workqueue.h当中,如下所示

    1. struct work_struct {
    2. atomic_long_t data; // 工作项的数据字段
    3. struct list_head entry; // 工作项在工作队列中的链表节点
    4. work_func_t func; // 工作项的处理函数
    5. #ifdef CONFIG_LOCKDEP
    6. struct lockdep_map lockdep_map; // 锁依赖性映射
    7. #endif
    8. };

        这些工作组织成工作队列,内核使用struct workqueue_struct结构体描述一个工作队列,定义在include/linux/workqueue.h 当中,如下所示:

    1. struct workqueue_struct {
    2. struct list_head pwqs; // 工作队列上的挂起工作项列表
    3. struct list_head delayed_works; // 延迟执行的工作项列表
    4. struct delayed_work_timer dwork_timer; // 延迟工作项的定时器
    5. struct workqueue_attrs *unbound_attrs; // 无绑定工作队列的属性
    6. struct pool_workqueue *dfl_pwq; // 默认的池化工作队列
    7. ...
    8. };

     

    45.2 工作队列相关接口函数

    在Linux内核中,create_workqueue函数用于创建一个工作队列,函数原型如下所示:

    struct workqueue_struct *create_workqueue(const char *name);

    参数name是创建的工作队列的名字。使用这个函数可以给每个CPU都创建一个CPU相关的工作队列。创建成功返回一个struct workqueue_struct类型指针,创建失败返回NULL。

    如果只给一个CPU创建一个CPU相关的工作队列,使用以下函数。

    #define create_singlethread_workqueue(name) \ alloc_workqueue("%s", WQ_SINGLE_THREAD, 1, name)

    参数name是创建的工作队列的名字。使用这个函数只会给一个CPU创建一个CPU相关的工作队列。创建成功之后返回一个struct workqueue_struct 类型指针,创建失败返回NULL。

    当工作队列创建好之后,需要将要延迟执行的工作项放在工作队列上,调度工作队列,使用queue_work_on函数,函数原型如下所示:

    bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work);

     该函数有三个参数,第一个参数是一个整数cpu,第二个参数是一个指向struct workqueue_struct的指针wq,第三个参数是一个指向struct work_struct的指针work。

    该函数的返回类型是布尔值,表示是否成功调度工作队列。 queue_work_on函数还有其他变种,比如queue_work函数,这里略过,其实思路是一致的,用于将定义好的工作项立即添加到工作队列中,并在工作队列可用时立即执行。

    如果要取消一个已经调度的工作,使用函数bool cancel_work_sync,函数原型如下所示:

    bool cancel_work_sync(struct work_struct *work);

     函数的作用是取消一个已经调度的工作,如果被取消的工作已经正在执行,则会等待他执行完成再返回。

    在Linux 内核中,使用flush_workqueue函数将刷新该工作队列中所有已提交但未执行的工作项。函数原型如下所示:

    void flush_workqueue(struct workqueue_struct *wq);

     该函数参数是一个指向struct workqueue_struct类型的指针wq。函数的作用是刷新工作队列,告诉内核尽快处理工作队列上的工作。

    如果要删除自定义的工作队列,使用destroy_workqueue函数,函数原型如下所示:

    void destroy_workqueue(struct workqueue_struct *wq);

    该函数参数是一个指向struct workqueue_struct类型的指针wq。

    在下一小节中将使用上述工作队列API函数进行相应的实验。

    45.3 实验程序的编写

    45.3.1 驱动程序编写

    本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\35_workqueue_share\module

    本实验将实现注册显示屏触摸中断,每按当触摸LCD显示屏就会触发中断服务函数,在中断服务函数中提交工作项到工作队列中,打印“This id test_interrupt”和“This is test_work”。

    在驱动程序中的模块初始化函数中,我们将GPIO转换为中断号,并使用request_irq函数请求中断,然后创建自定义工作队列,初始化工作项。当中断被触发时,中断处理函数被调用,并将工作项提交到自定义工作队列中,最终由工作项处理函数异步执行。编写完成的interrupt.c代码如下所示,添加的代码已加粗表示。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int irq;
    8. struct workqueue_struct *test_workqueue;
    9. struct work_struct test_workqueue_work;
    10. // 工作项处理函数
    11. void test_work(struct work_struct *work)
    12. {
    13. msleep(1000);
    14. printk("This is test_work\n");
    15. }
    16. // 中断处理函数
    17. irqreturn_t test_interrupt(int irq, void *args)
    18. {
    19. printk("This is test_interrupt\n");
    20. // 提交工作项到自定义工作队列
    21. queue_work(test_workqueue, &test_workqueue_work);
    22. return IRQ_RETVAL(IRQ_HANDLED);
    23. }
    24. static int interrupt_irq_init(void)
    25. {
    26. int ret;
    27. irq = gpio_to_irq(101); // 将GPIO映射为中断号
    28. printk("irq is %d\n", irq);
    29. // 请求中断
    30. ret = request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, "test", NULL);
    31. if (ret < 0)
    32. {
    33. printk("request_irq is error\n");
    34. return -1;
    35. }
    36. test_workqueue = create_workqueue("test_workqueue"); // 创建工作队列
    37. INIT_WORK(&test_workqueue_work, test_work); // 初始化工作项
    38. return 0;
    39. }
    40. static void interrupt_irq_exit(void)
    41. {
    42. free_irq(irq, NULL); // 释放中断
    43. cancel_work_sync(&test_workqueue_work); // 取消工作项
    44. flush_workqueue(test_workqueue); // 刷新工作队列
    45. destroy_workqueue(test_workqueue); // 销毁工作队列
    46. printk("bye bye\n");
    47. }
    48. module_init(interrupt_irq_init);
    49. module_exit(interrupt_irq_exit);
    50. MODULE_LICENSE("GPL");
    51. MODULE_AUTHOR("topeet");

    45.4 运行测试

    45.4.1 编译驱动程序

    在上一小节中的interrupt.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:

    1. export ARCH=arm64#设置平台架构
    2. export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
    3. obj-m += interrupt.o #此处要和你的驱动源文件同名
    4. KDIR :=/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
    5. PWD ?= $(shell pwd)
    6. all:
    7. make -C $(KDIR) M=$(PWD) modules #make操作
    8. clean:
    9. make -C $(KDIR) M=$(PWD) clean #make clean操作

    对于Makefile的内容注释已在上图添加,保存退出之后,来到存放interrupt.c和Makefile文件目录下,如下图(图45-1)所示:

    然后使用命令“make”进行驱动的编译,编译完成如下图(图45-2)所示:

    编译完生成interrupt.ko目标文件,如下图(图45-3)所示:

    至此驱动模块就编译成功了,接下来进行测试。

    45.4.2 运行测试

    开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图45-4)所示:

    insmod interrupt.ko

    加载驱动之后,可以看到申请的中断号被打印了出来,然后用手触摸连接的LVDS 7寸屏幕,打印如下图(45-5)所示: 

    在上图中,可以看到打印中断处理函数中添加的打印“This is test_interrupt”被打印了俩次,说明触发了2次中断上文,那么中断上文会调度2次中断下文,所以也会打印2次工作项处理函数中添加的打印“This is test_work”。

    在按屏幕之后,立即输入ps -aux|grep test_workqueue命令可以查看自己创建的工作队列,如下(图 45-6)所示:

    最后可以使用以下命令进行驱动的卸载,如下图(图45-7)所示:

    rmmod interrupt

     

    至此,自定义工作队列实验就完成了。 


     

  • 相关阅读:
    【LVGL事件(Events)】事件在不同组件上的应用(一)
    排序算法(Python实现)
    【正则表达式】笔记
    前端静态页面基本开发思路(一)
    104. Go单测系列4---编写可测试的代码
    MySql的InnoDB的三层B+树可以存储两千万左右条数据的计算逻辑
    NNDL 作业10:第六章课后题(LSTM | GRU)
    sed -i 使用变量进行替换
    【Linux】指针常量和常量指针
    ArmSoM-RK3588编解码之mpp编码demo解析:mpi_enc_test
  • 原文地址:https://blog.csdn.net/BeiJingXunWei/article/details/133384795