在Linux设备驱动中,使用中断的设备需要申请和释放对应的中断,分别使用内核提供的request_irq()和free_irq()函数。 - int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);
复制代码 irq:要申请的硬件中断号。 handler:向系统登记的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。 irqflags:中断处理的属性,若设置了SA_INTERRUPT,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了SA_SHIRQ,则表示多个设备共享中断,dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。 devname:中断描述文件,每一个中断可指定一种中断描述名。 dev_id:传递给handler或设置为NULL。 request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY 表示中断已经被占用且不能共享。 与request_irq()向对应的函数为free_irq(),它的原型如下: - void free_irq(unsigned int irq,void *dev_id);
复制代码free_irq()中参数的定义与request_irq()相同。 编写一个驱动模块,通过request_irq函数实现按键中断,卸载驱动时通过free_irq函数释放中断资源。 在kernel/drivers/char/x4412目录下新建x4412-eint.c文件,编辑内容如下: - #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/init.h>
- #include <linux/cdev.h>
- #include <asm/uaccess.h>
- #include <linux/slab.h>
- #include <linux/device.h>
- #include <mach/gpio.h>
-
- /* 定义并初始化等待队列头 */
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
- static struct class *cdrv_class;
- static struct device *cdrv_device;
- typedef struct pin_desc
- {
- unsigned int pin;
- unsigned int key_val;
- }pin_desc;
-
- static struct pin_desc pins_desc[6] =
- {
- {EXYNOS4_GPX1(0),0x01},//LEFT
- {EXYNOS4_GPX1(3),0x02},//RIGHT
- {EXYNOS4_GPX1(2),0x03},//UP
- {EXYNOS4_GPX1(1),0x04},//DOWN
- {EXYNOS4_GPX1(5),0x05},//MENU
- {EXYNOS4_GPX1(4),0x06},//BACK
- };
- /* 键值: 按下时, 0x01,0x02,0x03,0x04,0x05,0x06 */
- /* 键值: 松开时, 0x81,0x82,0x83,0x84,0x05,0x06 */
- static unsigned char key_val;
- static int ev_press = 0;
- int major;
- /* 用户中断处理函数 */
- static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
- {
- struct pin_desc *pindesc = (struct pin_desc *)dev_id;
- unsigned int pinval;
- printk("eint occured..\r\n");
- pinval = gpio_get_value(pindesc->pin);
- if(pinval)
- {
- key_val = 0x80 | (pindesc->key_val);/* 松开 */
- }
- else
- {
- key_val = pindesc->key_val;/* 按下 */
- }
- ev_press = 1; /* 表示中断已经发生 */
- wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
- return IRQ_HANDLED;
- }
-
- static int x4412_eint_open(struct inode * inode, struct file * filp)
- {
- return 0;
- }
- static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
- {
- if (count != 1)
- return -EINVAL;
- wait_event_interruptible(button_waitq, ev_press);
- if(copy_to_user(buf, &key_val, 1))
- return -EFAULT;
- ev_press = 0;
- return 1;
- }
- static int x4412_eint_release(struct inode *inode, struct file *file)
- {
- int irq;
- irq = gpio_to_irq(pins_desc[0].pin);
- free_irq(irq, &pins_desc[0]);
- irq = gpio_to_irq(pins_desc[1].pin);
- free_irq(irq,&pins_desc[1]);
- irq = gpio_to_irq(pins_desc[2].pin);
- free_irq(irq,&pins_desc[2]);
- irq = gpio_to_irq(pins_desc[3].pin);
- free_irq(irq, &pins_desc[3]);
- irq = gpio_to_irq(pins_desc[4].pin);
- free_irq(irq,&pins_desc[4]);
- irq = gpio_to_irq(pins_desc[5].pin);
- free_irq(irq,&pins_desc[5]);
- return 0;
- }
- static const struct file_operations x4412_eint_fops =
- {
- .owner = THIS_MODULE,
- .read = x4412_eint_read,
- .open = x4412_eint_open,
- .release = x4412_eint_release,
- };
- /* 驱动入口函数 */
- static int x4412_eint_init(void)
- {
- int ret,irq;
- major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
- cdrv_class = class_create(THIS_MODULE, "x4412-eint");
- cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
- irq = gpio_to_irq(pins_desc[0].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "LEFT", &pins_desc[0]);
- if(ret)
- printk("request irq_eint8 error!\r\n");
- irq = gpio_to_irq(pins_desc[1].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
- if(ret)
- printk("request irq_eint11 error!\r\n");
- irq = gpio_to_irq(pins_desc[2].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP", &pins_desc[2]);
- if(ret)
- printk("request irq_eint10 error!\r\n");
- irq = gpio_to_irq(pins_desc[3].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
- if(ret)
- printk("request irq_eint9 error!\r\n");
- irq = gpio_to_irq(pins_desc[4].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
- if(ret)
- printk("request irq_eint13 error!\r\n");
- irq = gpio_to_irq(pins_desc[5].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
- if(ret)
- printk("request irq_eint12 error!\r\n");
- return 0;
- }
- /* 驱动出口函数 */
- static void x4412_eint_exit(void)
- {
- unregister_chrdev(major, "x4412-key");
- device_unregister(cdrv_device); //卸载类下的设备
- class_destroy(cdrv_class); //卸载类
- }
- module_init(x4412_eint_init); //用于修饰入口函数
- module_exit(x4412_eint_exit); //用于修饰出口函数
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("GPL");
复制代码 驱动模块在x4412_eint_init函数中通过gpio_to_irq函数获取开发板上六个按键对应的GPIO口的中断号,再通过request_irq函数申请中断。在中断服务线程x4412_buttons_irq中通过gpio_get_value函数获取当前按键的按下或抬起状态,并保存在变量key_val中。在x4412_eint_read函数中通过copy_to_user函数向上层上报按键键值及按下或抬起的状态。 在驱动模块中添加了等待队列机制。程序开始声明并初始化了等待队列头: - static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
复制代码 在读函数中,调用wait_event_interruptible函数定义了一个等待事件,它允许被中断打断。 - wait_event_interruptible(button_waitq, ev_press);
复制代码 在应用程序通过read函数从驱动模块中读取键值时,如果没有中断产生,将一时处于等待状态。每当产生一次中断,ev_press将被清零,应用程序再次调用时,在没有中断产生的情况下又会进入等待状态。 在中断服务线程中,每当产生一次中断,将通过wake_up_interruptible函数唤醒等待队列,读函数中的等待队列被唤醒,应用程序将会读到按键键值。 将内核中默认的按键中断驱动取消,通过menuconfig即可配置: - Device Drivers ---> Input device support ---> Keyboards ---> GPIO Buttons
复制代码 取消选中即可,否则将会与现有驱动冲突。编辑kernel/drivers/char/x4412/Kconfig文件,添加以下内容: - config X4412_EINT_DRIVER
- tristate "x4412 eint driver"
- default m
- help
- compile for x4412 eint driver,y for kernel,m for module.
复制代码 编辑kernel/drivers/char/x4412/Makefile文件,添加以下内容: - obj-$(CONFIG_X4412_EINT_DRIVER) += x4412-eint.o
复制代码 编译内核,在kernel/drivers/char/x4412目录下将会生成目标映像x4412-eint.ko。 在ubuntu的用户目录或samba目录下新建测试程序x4412-eint-app.c文件,编辑内容如下: - #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- int main(int argc ,char *argv[])
- {
- int fd;
- unsigned char key_val;
- fd = open("/dev/x4412-eint",O_RDWR);
- if (fd < 0)
- {
- printf("open error\n");
- }
- while(1)
- {
- read(fd,&key_val,1);
- printf("key_val = 0x%x\n",key_val);
- }
- return 0;
- }
复制代码 使用如下指令编译应用程序: - arm-none-linux-gnueabi-gcc x4412-eint-app.c -o x4412-eint-app
复制代码 加载驱动模块,通过cat /proc/ interrupts指令可以查看驱动模块申请的中断信息: - [root@x4412 mnt]# cat /proc/interrupts
- CPU0 CPU3
- ……
- 360: 9 0 exynos-eint LEFT
- 361: 14 0 exynos-eint DOWN
- 362: 13 0 exynos-eint UP
- 363: 10 0 exynos-eint RIGHT
- 364: 8 0 exynos-eint BACK
- 365: 8 0 exynos-eint MENU
- 373: 1 0 exynos-eint eth0
- 383: 1 0 exynos-eint hpd
- ……
- [root@x4412 mnt]#
复制代码 第一列对应中断号,第二列和第三列表示在CPU0和CPU3上的中断次数,第四列表示可编程中断控制器,第五列表示设备名称,即request_irq的devname段。 按下开发板上六个独立按键的任意一个,将会输出打印信息“eintoccured..”,表明中断已经产生。执行应用程序,观察实验现象: - [root@x4412 mnt]# ./x4412-eint-app
- [ 3410.952271] eint occured..
- key_val = 0x3
- [ 3411.187970] eint occured..
- key_val = 0x83
- [ 3411.552708] eint occured..
- key_val = 0x4
- [ 3411.775483] eint occured..
- [ 3411.776791] eint occured..
- key_val = 0x84
复制代码 当没有按下按键,即没有中断产生时,不会有任何打印输出,这时read函数正在等待队列被唤醒。只有按下中断后,等待队列才会被唤醒,应用程序才能够读出按键键值并打印输出。
可直接在x4412开发板或ibox卡片电脑上测试的驱动模块和应用程序:注意内核也需要更新,否则中断会冲突。
x4412-eint.ko
(5.36 KB, 下载次数: 4)
x4412-eint-app
(5.97 KB, 下载次数: 4)
zImage.part1.rar
(1.9 MB, 下载次数: 5)
zImage.part2.rar
(1.74 MB, 下载次数: 5)
|