linux驱动由浅入深系列:中断申请及下半部处理机制
来源:互联网 发布:黄金投资软件 编辑:程序博客网 时间:2024/06/01 11:17
1, 中断申请
使用request_irq函数向系统申请中断。
intrequest_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname设置中断名称,通常是设备驱动程序的名称 在cat/proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
2, 中断下半部的引入
中断上半部执行过程中是会关闭同级别的中断的,为避免期间同级别中断丢失,需要尽快退出。但中断中有时又存在大量逻辑需要处理,这就引入了中断下半部的概念。在中断上半部尽快读取硬件寄存器,完成硬件操作,然后退出,将大量逻辑操作推迟到下半部完成。
3, 中断下半部的实现方法
a,软中断机制(softirq)
b,小任务机制(tasklet)
c,工作队列机制(workqueue)
4, workqueue
在实际使用中工作队列最为通用,因为softirq、tasklet实际机制类似,响应快于普通进程,期间不允许调度,而一般驱动开发中使用的中断中并没有如此高的要求。
Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。workqueue是内核中实现简单而有效的机制,他显然简化了内核daemon的创建,方便了用户的编程。
工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。
5, workqueue使用方法
INIT_WORK()对应queue_work、schedule_work()
INIT_DELAYED_WORK()对应queue_delayed_work、schedule_delay_work后者就是专门用于可以有延时的,而前者就是没有延时的
下面是一个使用workqueue的实例:
#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <linux/input.h>#include <linux/workqueue.h>#include <linux/gpio.h>#include <linux/interrupt.h>#define DRIVER_NAME "hello"#define DEVICE_NAME "hello"#define GPIO_KEY72MODULE_LICENSE("Dual BSD/GPL");MODULE_AUTHOR("Radia");static struct input_dev *input;static struct hello_platform_data *pdata;struct hello_platform_data {//设备结构体中的用户自定义结构体 定义 int gpio; struct work_structhello_wq_work;};static struct hello_platform_data hello_pdata = { //设备结构体中的用户自定义结构体 填入初始数据 .gpio = GPIO_KEY,};static struct platform_device hello_device = {//设备结构体填入初始数据.name = DRIVER_NAME,.id = -1,.dev = {.platform_data = &hello_pdata,//用户自定义结构体 加入到设备结构体中}};static void hello_wq_work_func(struct work_struct * work){ printk("enter hello_wq_work_func!\n"); int state = (gpio_get_value_cansleep(pdata->gpio) ? 1 : 0) ^ 1; printk(KERN_EMERG "<0>hello report event val:%d\n", state);input_report_key(input, KEY_VOLUMEUP, !!state);input_sync(input);}static irqreturn_t button_interrupt(int irq, void *dev_id){ printk("enter button_interrupt!\n"); schedule_work(&pdata->hello_wq_work);return IRQ_HANDLED;}static int hello_probe(struct platform_device *pdv)//在probe时,设备结构体被以参数的形式传递了进来,同时自定义的hello_platform_data也在其中了{ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); pdata = pdv->dev.platform_data;//获取到hello_platform_data结构体指针int irq = gpio_to_irq(pdata->gpio);printk(KERN_EMERG "hello key probe\n"); gpio_request(pdata->gpio, "gpio_key_test_button"); gpio_direction_input(pdata->gpio);request_irq(irq, button_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);input = input_allocate_device();set_bit(EV_KEY, input->evbit);input->name = "hello_gpio_key";input->id.bustype = BUS_HOST;set_bit(KEY_VOLUMEUP, input->keybit);input_register_device(input);INIT_WORK(&pdata->hello_wq_work, hello_wq_work_func);return 0;}static int hello_remove(struct platform_device *pdv){struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdv);printk(KERN_EMERG "hello key remove\n");input_unregister_device(input);return 0;}static void hello_shutdown(struct platform_device *pdv){}static int hello_suspend(struct platform_device *pdv, pm_message_t pmt){return 0;}static int hello_resume(struct platform_device *pdv){return 0;}static struct platform_driver hello_driver = {//驱动结构体填入初始数据.probe = hello_probe,.remove = hello_remove,.shutdown = hello_shutdown,.suspend = hello_suspend,.resume = hello_resume,.driver = {.name = DRIVER_NAME,.owner = THIS_MODULE,}};static int hello_init(void){int driver_state;printk(KERN_EMERG "hello module has been mount!\n");platform_device_register(&hello_device);//使用设备结构体中的数据将设备进行注册printk(KERN_EMERG "platform_device_register end\n");driver_state = platform_driver_register(&hello_driver);//使用驱动结构体中的数据将驱动进行注册printk(KERN_EMERG "platform_driver_register driver_state is %d\n", driver_state);return 0;}static void hello_exit(void){printk(KERN_EMERG "hello module has been remove!\n");platform_driver_unregister(&hello_driver);}module_init(hello_init);module_exit(hello_exit);
- linux驱动由浅入深系列:中断申请及下半部处理机制
- Linux中断处理“下半部”机制
- linux驱动-中断机制
- Linux驱动中断机制
- linux驱动由浅入深系列:camera驱动之一(背景及基础知识篇)
- 驱动-linux 中断处理
- Linux 驱动中断处理
- linux中断处理机制
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- 【linux】设备驱动动态中断申请
- Linux设备驱动中断机制
- 《Linux设备驱动开发详解》-- Linux中断处理底半部机制(tasklet、工作队列和软中断)
- linux驱动中中断处理
- Linux 设备驱动--- 中断处理
- Linux中断处理驱动设计
- [Linux驱动入门]中断处理
- 中断处理“下半部”机制
- Android DJI Mobile-SDK 开发
- 十大Material Design开源项目
- Python 将拼音转换成汉字
- 5-6 混合类型数据格式化输入 (5分)
- Javascript--01
- linux驱动由浅入深系列:中断申请及下半部处理机制
- c++ 利用libcurl实现文件断点续传
- MySQL系列教程二---触发器
- 5-8 超速判断 (10分)
- Tomcat域名或IP地址访问方式配置方法
- Exercise for Core Jave(Core Jave 练习题)
- Struts 2.5 使用DMI遇到的问题
- Data Integration学习笔记 --- 数据集成后数据校验细节
- [LeetCode]121 Best Time to Buy and Sell Stock