Linux中断
Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操
作。tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。内核定时器也
依靠软中断实现。
1.申请和释放中断
申请中断 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
irq 是要申请的硬件中断号。
handler 是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用 这个函数,
dev_id 参数将被传递给它。
irqflags是中断处理的属性,可以指定中断的触发方式以及处理方式。在触发方式方面,可以是 IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_ LOW 等。在处理方式方面,若设置了 IRQF_DISABLED,表明中断处理程序是快速处理程序,快 速处理程序被调用时屏蔽所有中断,慢速处理程序则不会屏蔽其他设备的驱动;若设置了 IRQF_SHARED,则表示多个设备共享中断,
dev_id 在中断共享时会用到,一般设置为这个设备 的设备结构体或者 NULL。
request_irq()返回 0 表示成功,返回-EINVAL 表示中断号无效或处理函数指针为 NULL,返 回-EBUSY 表示中断已经被占用且不能共享。
顶半部 handler 的类型 irq_handler_t 定义为:
typedef irqreturn_t (*irq_handler_t)(int, void *);
typedef int irqreturn_t;
释放中断
void free_irq(unsigned int irq,void *dev_id);
2.使能和屏蔽中断
void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq); disable_irq_nosync()与 disable_irq()的区别在于前者立即返回,而后者等待目前的中断处理完 成。由于 disable_irq()会等待指定的中断被处理完,因此如果在 n 号中断的顶半部调用 disable_irq(n),会引起系统的死锁,这种情况下,只能调用 disable_irq_nosync(n)。
下列两个函数(或宏,具体实现依赖于 CPU 体系结构)将屏蔽本 CPU 内的所有中断:
#define local_irq_save(flags) ...
void local_irq_disable(void);
与上述两个禁止中断对应的恢复中断的函数(或宏)是:
#define local_irq_restore(flags) ...
void local_irq_enable(void);
3.底半部机制
Linux 实现底半部的机制主要有 tasklet、工作队列和软中断。
(1)tasklet
tasklet 的使用较简单,我们只需要定义 tasklet 及其处理函数并将两者关联,例如:
void my_tasklet_func(unsigned long); /*定义一个处理函数*/
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
/*定义一个 tasklet 结构 my_tasklet,与 my_tasklet_func(data)函数相关联 */
代码 DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)实现了定义名称为 my_tasklet 的
tasklet 并将其与 my_tasklet_func()这个函数绑定,而传入这个函数的参数为 data。
在需要调度 tasklet 的时候引用一个 tasklet_schedule()函数就能使系统在适当的时候进行调度
运行:
tasklet_schedule(&my_tasklet);
(2).工作队列 工作队列的使用方法和 tasklet 非常相似,下面的代码用于定义一个工作队列和一个底半部执 行函数:
struct work_struct my_wq; /*定义一个工作队列*/
void my_wq_func(unsigned long); /*定义一个处理函数*/
通过 INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:
INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL); /*初始化工作队列并将其与处理函数绑定*/
与 tasklet_schedule()对应的用于调度工作队列执行的函数为 schedule_work(),如:
schedule_work(&my_wq);/*调度工作队列执行*/
(3).软中断
软中断(softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,asklet 是基于软中断实现的,因此也运行于软中断上下文。
在 Linux 内核中,用 softirq_action 结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用 open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。
、
软中断和 tasklet 运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进
程上下文。因此,软中断和 tasklet 处理函数中不能睡眠,而工作队列处理函数中允许睡眠。
按键中断驱动程序:
四个按键(对应的IO口为GPH2(0),GPH2(1),GPH2(2),GPH2(3))) 分别对应外部中断为EINT16,17,18,19
加入了poll机制和异步通知fasync
#include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <linux/gpio.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #define KEYNAME "mykey" static unsigned char keymajor = 0; static struct cdev key_cdev; static struct class *key_class; static struct device*key_device; static struct fasync_struct *fasync_queue; static DECLARE_WAIT_QUEUE_HEAD(my_key_waitq); /*DECLARE_WAIT_QUEUE_HEAD (name)声明且初始化*/ static unsigned char pressed = 0; static unsigned char key_value = 0; struct my_key_desc { unsigned int irq; unsigned int pin; unsigned char value; const char* name; }; static struct my_key_desc key_desc[] = { {IRQ_EINT16_31, S5PV210_GPH2(0), 0x01, "KEY1"}, {IRQ_EINT16_31, S5PV210_GPH2(1), 0x02, "KEY2"}, {IRQ_EINT16_31, S5PV210_GPH2(2), 0x03, "KEY3"}, {IRQ_EINT16_31, S5PV210_GPH2(3), 0x04, "KEY4"}, }; static irqreturn_t my_key_irq(int irq, void* dev_id) { volatile struct my_key_desc key_dr = *(volatile struct my_key_desc*)dev_id; int value; value = gpio_get_value(key_dr.pin); if(value == 0) /*有键按下key_desc.value = 0x8x*/ key_value = key_dr.value|0x80; else key_value = key_dr.value ; pressed = 1; wake_up_interruptible(&my_key_waitq); kill_fasync(&fasync_queue, SIGIO, POLLIN | POLLOUT); return 0; } static int my_key_open(struct inode *inode, struct file *file) { int ret; printk("request_irq setting\n"); ret = request_irq( IRQ_EINT(16), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY1", (void*)&key_desc[0]); if(ret) { printk("ret is %d\n", ret); return ret; } ret = request_irq( IRQ_EINT(17), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY2", (void*)&key_desc[1]); if(ret) return ret; ret = request_irq( IRQ_EINT(18), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY3", (void*)&key_desc[2]); if(ret) return ret; ret = request_irq( IRQ_EINT(19), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY4", (void*)&key_desc[3]); if(ret) return ret; return 0; } /*static int my_key_open(struct inode * inode, struct file * file) { int ret; unsigned char i; for(i = 0; i < 4; i++) { // s3c_gpio_cfg(key_desc[i].pin, EINT_MODE ); sizeof(key_desc) / sizeof(key_desc[0]) ret = request_irq(key_desc[i].irq, my_key_irq , IRQ_TYPE_EDGE_BOTH, key_desc[i].name,(void*)&key_desc[i]); if(ret) { printk(KERN_EMERG"request_irq error %d\n" , i); break; } } if(ret) { i--; for(; i > 0; i--) { free_irq(key_desc[i].irq, (void*)&key_desc[i]); return -EBUSY; } } return 0; }*/ static int my_key_fasync(int fd, struct file * file, int on){ return fasync_helper(fd, file, on, &fasync_queue); } static ssize_t my_key_read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { int ret; if(n != 1) { printk(KERN_EMERG"The driver can only give one key value once!\n"); return -EINVAL; } if(filep->f_flags & O_NONBLOCK) { if(!pressed) return -EBUSY; } else { wait_event_interruptible(my_key_waitq, pressed);/*pressed为1时继续执行后面,否则休眠*/ pressed = 0; ret = copy_to_user(outbuf, &key_value, 1); if(ret) { printk(KERN_EMERG"key copy_to_user error\n"); return -ENOMEM; } } return 0; } static int my_key_write(struct file * filep,const char * buf,size_t n,loff_t * ppos) { return 0; } static int my_key_close(struct inode * inode,struct file * file) { free_irq(IRQ_EINT(16), (void*)&key_desc[0]); free_irq(IRQ_EINT(17), (void*)&key_desc[1]); free_irq(IRQ_EINT(18), (void*)&key_desc[2]); free_irq(IRQ_EINT(19), (void*)&key_desc[3]); my_key_fasync(-1, file, 0); return 0; } static unsigned int my_key_poll(struct file *filep, poll_table *wait) { unsigned int mask = 0; poll_wait(filep, &my_key_waitq, wait); if(pressed) { mask |= POLLIN | POLLRDNORM; } return mask; } struct file_operations key_fops= { .owner = THIS_MODULE, .open = my_key_open, .read = my_key_read, .write = my_key_write, .release = my_key_close, .poll = my_key_poll, .fasync = my_key_fasync, }; static int __init my_key_init(void) { int ret; int deno; deno = MKDEV(keymajor, 0); cdev_init(&key_cdev, &key_fops); if (keymajor) { register_chrdev_region(deno, 1, KEYNAME); printk(KERN_EMERG"key major is %d\n", keymajor); } else { alloc_chrdev_region(&deno, 0, 1, KEYNAME); keymajor = MAJOR(deno); printk(KERN_EMERG"key major is %d\n", keymajor); } key_cdev.owner = THIS_MODULE; key_cdev.ops = &key_fops; ret = cdev_add(&key_cdev, deno, 1); if(ret) { printk(KERN_EMERG"cdev_add error\n"); goto add_error; } key_class= class_create(THIS_MODULE, KEYNAME); if(IS_ERR(key_class)) { printk(KERN_EMERG"class_create error\n"); goto class_error; } key_device= device_create(key_class, NULL, deno, NULL, KEYNAME); if(IS_ERR(key_device)) { printk(KERN_EMERG"device_create error\n"); goto device_error; } // init_waitqueue_head(&my_key_waitq); return 0; device_error: class_destroy(key_class); class_error: cdev_del(&key_cdev); add_error: unregister_chrdev_region(deno,1); return -ENODEV; } static void __exit my_key_exit(void) { device_destroy(key_class, MKDEV(keymajor, 0)); class_destroy(key_class); cdev_del(&key_cdev); unregister_chrdev_region(MKDEV(keymajor, 0), 1); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("qigaohua"); module_init(my_key_init); module_exit(my_key_exit);
中断poll测试程序:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> int main() { int fd; int ret; unsigned char key_value; struct pollfd fds[1]; fd = open("/dev/mykey", O_RDWR); if(fd < 0) { perror("/dev/mykey open error\n"); return 0; } fds[0].fd = fd; fds[0].events = POLLIN; while(1) { ret = poll(fds, 1, 5000); if(ret == 0) { printf("timeout\n"); }else { read(fd, &key_value, 1); printf("key_value = 0x%x\n", key_value); } } }
异步通知fasync中断测试程序:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> int main() { int oflags; fd = open("/dev/mykey", O_RDWR); if(fd < 0) { printf("/dev/mykey open error\n"); return -EBUSY; } signal(SIGIO, my_sinal_fun); fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); while(1) { sleep(5); printf("hello\n"); } }
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Smart210学习记录——-linux驱动中断 - Python技术站