设备驱动的中断事件处理如下图所示,他与普通驱动的不同之处在于多了个数据缓冲区,驱动程序对上层提供的read/write方法并不直接完成硬件的数据操作,中断处理程序也是相对独立,他们通过缓冲区交换数据。而数据缓冲区的数据可通过FIFO进行读和写的操作,但每次只能进行读或者写的操作,两者不可同时进行,这就又涉及到了并发请求。并发请求类似信号量中的PV操作,对数据缓冲区的读写就相当于之前的生产者和消费者到仓库的存取。
中断事件涉及的函数 头文件 #include <asm/irq.h> 申请中断函数,成功返回0 int request_irq(unsigned int irq, void(*handler)(int,void*,struct pt_regs*), unsigned long irqflag, const char *devname, void *dev_id); //irq:中断号
// irqflag:
// SA_INTERRUPT,快速中断,执行handler函数时不能被打断
// SA_SHIRQ,共享中断, ,执行handler函数时能被打断
// SA_SAMPLE_RANDOM,中断可能被用来产生随机数
// dev_id: 在共享中断时用于区分不同的中断处理程序
// devname:中断设备名,在中断下运行cat /dev/interrupt 可查看到
释放中断函数 void free_irq(unsigned int irq, void *dev_id); #include <asm/irq.h> 使能中断函数 void enable_irq(unsigned int irq); 禁止中断函数 void disable_irq(unsigned int irq);
S3C2410使能GPIO的外部中断功能 int set_external_irq(int irq, int edge, int pullup); // edge:
// EXT_LOWLEVEL,
// EXT_HIGHLEVEL,
// EXT_FALLING_EDGE,
// EXT_RISING_EDGE,
// EXT_BOTH_EDGES
中断处理程序示例 void handler(int irq, void *dev_id, struct pt_regs *regs) { // 中断处理
} 在初始化时申请并初始化中断 set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0); request_irq(IRQ_EINT0,handler,SA_INTERRUPT,"KEY",NULL); 注意:要把使能外部中断函数set_external_irq放在中断请求函数request_irq之前,否则,会有小麻烦。 并发请求涉及的函数 使用信号量控制并发请求 #include <asm/semaphore.h> 定义信号量变量 struct semaphore bufflock; 初始化信号量 void sema_init(struct semaphore *sem, int); 获取(等待)信号量(不可打断)(相当于V操作) void down(struct semaphore *sem); 获取(等待)信号量(可被打断) int down_interruptible(struct semaphore *sem); 释放信号量(相当于P操作) void up(struct semaphore *sem); FIFO队列 由于队列具有先进先出的功能,所以它能符合充当数据缓冲区的功能。我们可以自己写一个FIFO函数实现读和写的功能,也可调用/kernel/kernel/fifo.c 的函数。如果要调用/kernel/kernel/fifo.c,编译时要注意,可用命令进行:/usr/local/arm/2.95.3/bin/arm-linux-gcc -c -o kfifo.o kfifo.c -I/home/su/kernel/include -D__KERNEL__ -DMODULE -DEXPORT_SYMTAB –DMODVERSIONS。当我们完成编译后,要先加载kfifo.o,再加载我们自己写的驱动,否则会出错。以下以调用/kernel/kernel/fifo.c为例。 #include <linux/kfifo.h> 首先需要定义一个自旋锁,该锁由kfifo维护,我们只需要定义它即可
static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED; 然后定义一个kfifo指针, 注意, 这里定义指针即可,不用分配空间
static struct kfifo *buffer; 使用kfifo_alloc可以创建一个BUFFER_SIZE大小的fifo, 所有空间由kfifo自动分配
#define BUFFER_SIZE 256 buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock); 使用kfifo_put可以将数据放入kfifo内 kfifo_put(buffer, &key, sizeof(key));
使用kfifo_len可以检查fifo内的可用数据 if(kfifo_len(buffer) >= sizeof(key))
使用kfifo_get可以从fifo内取出数据 kfifo_get(buffer, &key, sizeof(key));
最后退出时,释放buffer的内存空间 kfifo_free(buffer); 附:基于凌阳SPCE3200实验箱的4x4键盘的驱动(中断法实现)源代码 这里有两个需要注意的地方: 1、头文件的摆放顺序:#include <linux/kfifo.h>要放在 #include <asm/irq.h>之后,否则编译出错,因为asm/irq.h 里面定义了一些linux/kfifo.h 要用到的宏,若先写#include <linux/kfifo.h> ,加载驱动时,编译器会把里头的宏当成函数处理,但又找不到相应的函数,所以出错。这样的头文件出错,出错非常隐蔽,解决的办法唯有一步一步跟错查找。
2、该驱动的等待信号量函数应使用可打断类型的int down_interruptible(struct semaphore *sem),否则运行应用程序时,按下ctrl+c程序没法正常退出,还需按下实验箱上的任意一个按键。因为该中断是可被打断类型的,所以能接受响应外部的另外的中断,即能退出程序。 #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif
#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> /* printk() */ #include <linux/init.h> /* __init __exit */ #include <linux/types.h> /* size_t */ #include <linux/fs.h> /* file_operation */ //#include /* Error number */
#include <linux/delay.h> /* udelay */ #include <linux/timer.h> #include <asm/uaccess.h> /* copy_to_user, copy_from_user */ #include <asm/hardware.h> #include <asm/semaphore.h> #include <asm/irq.h> #include <asm/arch/S3C2410.h> #include <linux/kfifo.h>
#define DRIVER_NAME "key" #ifdef DEBUG #define PRINTK(fmt, arg...) printk(KERN_NOTICE fmt, ##arg) #else #define PRINTK(fmt, arg...) #endif #define BUFFER_SIZE 256
static int keyDriver_Major = 0; /* Driver Major Number */ static int keyNum; struct semaphore bufflock; static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED; static struct kfifo *buffer;
void handler(int irq, void *dev_id, struct pt_regs *regs) { int c_key,r_key,key; disable_irq(IRQ_EINT0); disable_irq(IRQ_EINT1); disable_irq(IRQ_EINT2); disable_irq(IRQ_EINT3); GPFCON = 0x0055; GPFDAT = 0xf0; mdelay(1); c_key = GPFDAT & 0xf0; //获得行标低四位
GPFCON = 0x5500; GPFDAT = 0x0f; udelay(30); r_key = GPFDAT & 0x0f; //获得列标高四位
key = ~( c_key | r_key) & 0xff; // PRINTK("key = 0x%x\n", key);
switch (key) { case 0x88: PRINTK("key16 \n"); keyNum = 16; break; case 0x84: PRINTK("key15 \n"); keyNum = 15; break; case 0x82: PRINTK("key14 \n"); keyNum = 14; break; case 0x81: PRINTK("key13 \n"); keyNum = 13; break; case 0x48: PRINTK("key12 \n"); keyNum = 12; break; case 0x44: PRINTK("key11 \n"); keyNum = 11; break; case 0x42: PRINTK("key10 \n"); keyNum = 10; break; case 0x41: PRINTK("key9 \n"); keyNum = 9; break; case 0x28: PRINTK("key8 \n"); keyNum = 8; break; case 0x24: PRINTK("key7 \n"); keyNum = 7; break; case 0x22: PRINTK("key6 \n"); keyNum = 6; break; case 0x21: PRINTK("key5 \n"); keyNum = 5; break; case 0x18: PRINTK("key4 \n"); keyNum = 4; break; case 0x14: PRINTK("key3 \n"); keyNum = 3; break; case 0x12: PRINTK("key2 \n"); keyNum = 2; break; case 0x11: PRINTK("key1 \n"); keyNum = 1; break; default: break; } if(keyNum > 0) { kfifo_put(buffer, &keyNum, sizeof(keyNum)); up(&bufflock); } //PRINTK("keyNum = %d\n", keyNum);
//PRINTK("GPFCON = 0x%x GPFUP = 0x%x GPGDAT = 0x%x\n", GPFCON, GPFUP, GPFDAT);
// GPFCON = (GPFCON | 0xffff) & 0x55AA;
GPFCON = 0x55AA; GPFUP=0x00; GPFDAT = 0x00; enable_irq(IRQ_EINT0); enable_irq(IRQ_EINT1); enable_irq(IRQ_EINT2); enable_irq(IRQ_EINT3); } /* Driver Operation Functions */ static int keyDriver_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT;
PRINTK("keyDriver open called!\n"); enable_irq(IRQ_EINT0); enable_irq(IRQ_EINT1); enable_irq(IRQ_EINT2); enable_irq(IRQ_EINT3); sema_init(&bufflock, 0); buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock); return 0; } static int keyDriver_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; PRINTK("keyDriver release called!\n"); disable_irq(IRQ_EINT0); disable_irq(IRQ_EINT1); disable_irq(IRQ_EINT2); disable_irq(IRQ_EINT3); kfifo_free(buffer); return 0; } static ssize_t keyDriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { size_t read_size = count; PRINTK("myDriver read called!\n"); PRINTK("\tcount=%d, pos=%d\n", count, (int)*f_pos); down_interruptible(&bufflock); //若这里使用down的话,运行应用程序时会出点小问题
kfifo_get(buffer, &keyNum, sizeof(keyNum)); copy_to_user(buf, &keyNum, read_size); *f_pos += read_size; return read_size; } static struct file_operations keyDriver_fops = { owner: THIS_MODULE, // write: keyDriver_write,
read: keyDriver_read, // ioctl: keyDriver_ioctl,
open: keyDriver_open, release: keyDriver_release, }; #ifdef CONFIG_DEVFS_FS devfs_handle_t devfs_keyDriver_dir; devfs_handle_t devfs_keyDriver_raw; #endif static int __init myModule_init(void) { GPFCON = 0x5500; //GPF4,5,6,7 设置为输出
GPFUP = 0x00; //GPF4、5、6、7上拉使能
GPFDAT = 0x00; //GPF4,5,6,7 输出低电平
set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0); request_irq(IRQ_EINT0, handler, SA_INTERRUPT, "KEY1", NULL); set_external_irq(IRQ_EINT1, EXT_FALLING_EDGE, 0); request_irq(IRQ_EINT1, handler, SA_INTERRUPT, "KEY2", NULL); set_external_irq(IRQ_EINT2, EXT_FALLING_EDGE, 0); request_irq(IRQ_EINT2, handler, SA_INTERRUPT, "KEY3", NULL); set_external_irq(IRQ_EINT3, EXT_FALLING_EDGE, 0); request_irq(IRQ_EINT3, handler, SA_INTERRUPT, "KEY4", NULL); /* Module init code */ PRINTK("myModule_init\n"); /* Driver register */ keyDriver_Major = register_chrdev(0, DRIVER_NAME, &keyDriver_fops); if(keyDriver_Major < 0) { PRINTK("register char device fail!\n"); return keyDriver_Major; } PRINTK("register keyDriver OK! Major = %d\n", keyDriver_Major); #ifdef CONFIG_DEVFS_FS devfs_keyDriver_dir = devfs_mk_dir(NULL, "keyDriver", NULL); devfs_keyDriver_raw = devfs_register(devfs_keyDriver_dir, "raw0", DEVFS_FL_DEFAULT, keyDriver_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &keyDriver_fops, NULL); PRINTK("add dev file to devfs OK!\n"); #endif return 0; } static void __exit myModule_exit(void) { /* Module exit code */ PRINTK("myModule_exit\n"); free_irq(IRQ_EINT0, NULL); free_irq(IRQ_EINT1, NULL); free_irq(IRQ_EINT2, NULL); free_irq(IRQ_EINT3, NULL); /* Driver unregister */ if(keyDriver_Major > 0) { #ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_keyDriver_raw); devfs_unregister(devfs_keyDriver_dir); #endif unregister_chrdev(keyDriver_Major, DRIVER_NAME); } return; } MODULE_AUTHOR("SXZ"); MODULE_LICENSE("Dual BSD/GPL"); module_init(myModule_init); module_exit(myModule_exit);
应用程序源代码: #include <stdio.h> #include <fcntl.h> #include <unistd.h>
int main(int argc, char *argv[]) { int fd; unsigned char key;
if((fd = open("/dev/keyDriver/raw0", O_RDONLY)) < 0) { printf("can't open keyDriver\n"); exit(1); } while(1) { read(fd, &key, 1); printf("program_key = %d\n", key); } close(fd); return 0; }
注:小志整理
|