在linux2.6内核驱动中,为设备实现一套中断处理机制提供了如下两个步骤:
1.向内核注册中断处理;2.实现中断处理函数
int request_irq(unsigned int irq,void (*handler)(int, void*, struct pt_regs *),unsigned long flags,const char *devname,void *dev_id) //注册中断;返回0表示成功,或者返回一个错误码
参数含义
unsigned int irq 中断号
void (*handler)(int,void *,struct pt_regs *) 中断处理函数
unsigned long flags 与中断管理有关的各种选项;如IRQF_DISABLED,IRQF_SHARED
const char * devname 设备名
void *dev_id 共享中断时使用
注意1 共享设备就是将不同的设备挂接到同一个中断信号线上,liunx对共享的支持主要是用在PCI设备服务。且在共享中断时,dev_id必须唯一指定。
注意2 共享中断的处理程序中,不能使用 disable_irq(unsigned int irq) 因为如果使用了这个函数,共享中断信线的其它设备将同样无法使用中断,也就无法正常工作了。
handler中断处理程序
中断处理程序就是普通的C代码。特别之处在于中断处理程序是在中断上下文中运行的,它的行为受到某些限制
1. 不能向用户空间发送或接受数据 2. 不能使用可能引起阻塞的函数 3. 不能使用可能引起调度的函数
中断处理函数一个实例如下
1 void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) 2 { 3 /* 判断是否是本设备产生了中断(为什么要做这样的检测? 共享中断) */ 4 value = inb(short_base); 5 if (!(value & 0x80)) return; 6 /* 清除中断位(如果设备支持自动清除,则不需要这步) */ 7 outb(value & 0x7F, short_base); 8 /* 中断处理,通常是数据接收*/ 9 。。。。。。。。。 10 /* 唤醒等待数据的进程*/ 11 wake_up_interruptible(&short_queue); 12 }
释放中断函数
void free_irq(unsigned int irq, void *dev_id) //当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们返还给系统
附s3c24xx按键中断驱动程序源码:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/poll.h> 7 #include <linux/irq.h> 8 #include <asm/irq.h> 9 #include <linux/interrupt.h> 10 #include <asm/uaccess.h> 11 #include <mach/regs-gpio.h> 12 #include <mach/hardware.h> 13 #include <linux/platform_device.h> 14 #include <linux/cdev.h> 15 #include <linux/miscdevice.h> 16 #include <linux/sched.h> 17 #include <linux/gpio.h> 18 19 #define DEVICE_NAME "buttons" 20 21 struct button_irq_desc { 22 int irq; 23 int pin; 24 int pin_setting; 25 int number; 26 char *name; 27 }; 28 29 //#define DEBUG 30 #undef DEBUG 31 #define QIUSHI 32 #define START_BUTTON 8 33 34 #ifdef QIUSHI 35 static struct button_irq_desc button_irqs [] = { 36 {IRQ_EINT0, S3C2410_GPF(0), S3C2410_GPF0_EINT0, 0, "KEY0"}, 37 {IRQ_EINT1, S3C2410_GPF(1), S3C2410_GPF1_EINT1, 1, "KEY1"}, 38 {IRQ_EINT2, S3C2410_GPF(2), S3C2410_GPF2_EINT2, 2, "KEY2"}, 39 {IRQ_EINT3, S3C2410_GPF(3), S3C2410_GPF3_EINT3, 3, "KEY3"}, 40 {IRQ_EINT4, S3C2410_GPF(4), S3C2410_GPF4_EINT4, 4, "KEY4"}, 41 {IRQ_EINT5, S3C2410_GPF(5), S3C2410_GPF5_EINT5, 5, "KEY5"}, 42 {IRQ_EINT6, S3C2410_GPF(6), S3C2410_GPF6_EINT6, 6, "KEY6"}, 43 {IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 7, "KEY7"}, 44 {IRQ_EINT9, S3C2410_GPG(1), S3C2410_GPG1_EINT9, 8, "KEY8"}, 45 }; 46 #else 47 static struct button_irq_desc button_irqs [] = { 48 {IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"}, 49 {IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"}, 50 {IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"}, 51 {IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"}, 52 {IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"}, 53 {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"}, 54 }; 55 #endif 56 57 static volatile char key_values [] = {'0', '0', '0', '0', '0', '0', '0', '0', '0'}; 58 59 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 60 61 static volatile int ev_press = 0; 62 63 64 static irqreturn_t buttons_interrupt(int irq, void *dev_id) 65 { 66 struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; 67 int down; 68 69 // udelay(0); 70 down = !s3c2410_gpio_getpin(button_irqs->pin); 71 72 #ifdef DEBUG 73 printk( "%s: new=%d, old=%d\n", __FUNCTION__ , down, (key_values[button_irqs->number] & 1)); 74 #endif 75 76 if (down != (key_values[button_irqs->number] & 1)) { // Changed 77 78 key_values[button_irqs->number] = '0' + down; 79 80 ev_press = 1; 81 wake_up_interruptible(&button_waitq); 82 } 83 return IRQ_RETVAL(IRQ_HANDLED); 84 } 85 86 87 static int s3c24xx_buttons_open(struct inode *inode, struct file *file) 88 { 89 int i; 90 int err = 0; 91 92 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { 93 if (button_irqs[i].irq < 0) { 94 continue; 95 } 96 err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 97 button_irqs[i].name, (void *)&button_irqs[i]); 98 if (err) 99 { 100 printk( "%s: err=%d, but=%d\n", __FUNCTION__, err, i); 101 break; 102 } 103 } 104 /*错误处理*/ 105 if (err) { 106 i--; 107 for (; i >= 0; i--) { 108 if (button_irqs[i].irq < 0) { 109 continue; 110 } 111 disable_irq(button_irqs[i].irq); 112 free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); 113 } 114 return -EBUSY; 115 } 116 117 ev_press = 1; 118 119 return 0; 120 } 121 122 123 static int s3c24xx_buttons_close(struct inode *inode, struct file *file) 124 { 125 int i; 126 127 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { 128 if (button_irqs[i].irq < 0) { 129 continue; 130 } 131 free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); 132 } 133 134 return 0; 135 } 136 137 138 static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 139 { 140 unsigned long err; 141 142 if (!ev_press) { 143 if (filp->f_flags & O_NONBLOCK) 144 return -EAGAIN; 145 else 146 wait_event_interruptible(button_waitq, ev_press); 147 } 148 149 ev_press = 0; 150 151 err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count)); 152 153 return err ? -EFAULT : min(sizeof(key_values), count); 154 } 155 156 /*poll做两件事?poll_wait指明使用的等待队列,返回掩码*/ 157 158 static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait) 159 { 160 unsigned int mask = 0; 161 poll_wait(file, &poll_wait, wait); 162 if (ev_press) /*按下evpress为1,有数据刻可读*/ 163 mask |= POLLIN | POLLRDNORM; 164 return mask; 165 } 166 167 168 169 170 static int s3c2410_buttons_ioctl( 171 struct inode *inode, 172 struct file *file, 173 unsigned int cmd, 174 unsigned long arg) 175 { int down; 176 down = s3c2410_gpio_getpin(button_irqs[START_BUTTON].pin); 177 return down; 178 } 179 180 181 static struct file_operations dev_fops = { 182 .owner = THIS_MODULE, 183 .open = s3c24xx_buttons_open, 184 .release = s3c24xx_buttons_close, 185 .read = s3c24xx_buttons_read, 186 .poll = s3c24xx_buttons_poll, 187 .ioctl = s3c2410_buttons_ioctl, 188 }; 189 190 191 static struct miscdevice misc = { 192 .minor = MISC_DYNAMIC_MINOR, 193 .name = DEVICE_NAME, 194 .fops = &dev_fops, 195 }; 196 197 static int __init dev_init(void) 198 { 199 int ret; 200 201 ret = misc_register(&misc); 202 203 printk (DEVICE_NAME"\tinitialized\n"); 204 205 return ret; 206 } 207 208 static void __exit dev_exit(void) 209 { 210 misc_deregister(&misc); 211 } 212 213 module_init(dev_init); 214 module_exit(dev_exit); 215 MODULE_LICENSE("GPL"); 216 MODULE_AUTHOR("QiuShi Inc.");
platform总线是linux2.6内核后加入的一条虚拟总线,由两部分组成:platform_device和platform_driver;优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,提高可可移植性。
结构体平台设备 struct platform_device
1 struct platform_device { 2 const char * name; 3 int id; 4 struct device dev; 5 u32 num_resources; 6 struct resource * resource; 7 8 const struct platform_device_id *id_entry; 9 10 /* arch specific additions */ 11 struct pdev_archdata archdata; 12 };
struct platform_device*platform_device_alloc(const char *name, int id) //平台设备分配函数
int platform_device_add(struct platform_device *pdev) 平台设备注册函数
平台设备资源 结构体 struct resource
1 struct resource { 2 resource_size_t start; //资源的起始物理地址 3 resource_size_t end; //资源的结束物理地址 4 const char *name; //资源的名称 5 unsigned long flags; //资源的类型;常用有MEM,IO,IRQ等 6 struct resource *parent, *sibling, *child; //资源的链表指针 7 };
设备资源分配实例:
1 static struct resource s3c_wdt_resource1 = { 2 3 .start = 0x44100000, 4 5 .end = 0x44200000, 6 7 .flags = IORESOURCE_MEM, 8 9 } 10 11 static struct resource s3c_wdt_resource2 = { 12 13 .start = 20, 14 15 .end = 20, 16 17 .flags = IORESOURCE_IRQ, 18 19 }
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) //获取资源分配函数
参数:
vdev: 资源所属的设备
vtype: 获取的资源类型
vnum: 获取的资源数
例:
platform_get_resource(pdev, IORESOURCE_IRQ, 0) //获取中断
结构体平台驱动 struct platform_driver
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 void (*shutdown)(struct platform_device *); 5 int (*suspend)(struct platform_device *, pm_message_t state); 6 int (*resume)(struct platform_device *); 7 struct device_driver driver; 8 const struct platform_device_id *id_table; 9 };
int platform_driver_register(struct platform_driver *) //平台驱动注册使用函数
void platform_driver_unregister(struct platform_driver *); //平台驱动卸载使用函数
下例结合mini2440利用平台驱动模型来分析按键中断程序
plat_device.c
1 #include <linux/module.h> 2 #include <linux/types.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/platform_device.h> 6 #include <mach/regs-gpio.h> 7 #include <linux/interrupt.h> 8 #include <linux/device.h> 9 #include <linux/io.h> 10 11 /*平台资源的定义*/ 12 static struct resource s3c_buttons_resource[] = { 13 [0]={ 14 .start = S3C24XX_PA_GPIO, 15 .end = S3C24XX_PA_GPIO + S3C24XX_SZ_GPIO - 1, 16 .flags = IORESOURCE_MEM, 17 }, 18 19 [1]={ 20 .start = IRQ_EINT8, 21 .end = IRQ_EINT8, 22 .flags = IORESOURCE_IRQ, 23 }, 24 [2]={ 25 .start = IRQ_EINT11, 26 .end = IRQ_EINT11, 27 .flags = IORESOURCE_IRQ, 28 }, 29 [3]={ 30 .start = IRQ_EINT13, 31 .end = IRQ_EINT13, 32 .flags = IORESOURCE_IRQ, 33 }, 34 [4]={ 35 .start = IRQ_EINT14, 36 .end = IRQ_EINT14, 37 .flags = IORESOURCE_IRQ, 38 }, 39 [5]={ 40 .start = IRQ_EINT15, 41 .end = IRQ_EINT15, 42 .flags = IORESOURCE_IRQ, 43 }, 44 [6]={ 45 .start = IRQ_EINT19, 46 .end = IRQ_EINT19, 47 .flags = IORESOURCE_IRQ, 48 } 49 }; 50 51 static struct platform_device *s3c_buttons; 52 53 54 static int __init platform_init(void) 55 { 56 57 s3c_buttons = platform_device_alloc("mini2440-buttons",-1); 58 59 platform_device_add_resources(s3c_buttons,&s3c_buttons_resource,7); 60 61 /*平台设备的注册*/ 62 platform_device_add(s3c_buttons); 63 64 65 } 66 67 static void __exit platform_exit(void) 68 { 69 platform_device_unregister(s3c_buttons); 70 } 71 72 module_init(platform_init); 73 module_exit(platform_exit); 74 75 MODULE_AUTHOR("David Xie"); 76 MODULE_LICENSE("GPL"); 77 MODULE_ALIAS("platform:mini2440buttons");
plat_buttons.c
1 #include <linux/module.h> 2 #include <linux/types.h> 3 #include <linux/miscdevice.h> 4 #include <linux/fs.h> 5 #include <linux/init.h> 6 #include <linux/platform_device.h> 7 #include <linux/interrupt.h> 8 #include <linux/clk.h> 9 #include <linux/uaccess.h> 10 #include <linux/io.h> 11 #include <mach/map.h> 12 #include <mach/regs-gpio.h> 13 #include <linux/poll.h> 14 #include <linux/irq.h> 15 #include <asm/unistd.h> 16 #include <linux/device.h> 17 18 19 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 20 21 static volatile int ev_press = 0; 22 23 24 static int key_value; 25 static struct device *buttons_dev; /* platform device attached to */ 26 static struct resource *buttons_mem; 27 static struct resource *buttons_irq; 28 static void __iomem *buttons_base; 29 30 31 static int button_irqs[6]; 32 33 static irqreturn_t buttons_interrupt(int irq, void *dev_id) 34 { 35 int i; 36 for(i=0; i<6; i++){ 37 if(irq == button_irqs[i]){ 38 //printk("==>interrput number:%d\n",irq); 39 key_value = i; 40 ev_press =1; 41 wake_up_interruptible(&button_waitq); 42 } 43 } 44 45 return IRQ_RETVAL(IRQ_HANDLED); 46 47 } 48 49 static int s3c24xx_buttons_open(struct inode *inode, struct file *file) 50 { 51 int i; 52 int err = 0; 53 /*注册中断*/ 54 for(i=0; i<6; i++){ 55 if (button_irqs[i] < 0) 56 continue; 57 58 /*中断触发方式:上升沿触发*/ 59 err = request_irq(button_irqs[i],buttons_interrupt,IRQ_TYPE_EDGE_RISING,NULL,NULL); 60 if(err) 61 break; 62 } 63 64 if (err) { 65 i--; 66 for (; i >= 0; i--) { 67 if (button_irqs[i] < 0) { 68 continue; 69 } 70 disable_irq(button_irqs[i]); 71 free_irq(button_irqs[i], NULL); 72 } 73 return -EBUSY; 74 } 75 76 ev_press = 0; 77 return 0; 78 } 79 80 static int s3c24xx_buttons_close(struct inode *inode, struct file *file) 81 { 82 int i; 83 for (i=0; i<6; i++) { 84 if (button_irqs[i] < 0) { 85 continue; 86 } 87 free_irq(button_irqs[i],NULL); 88 } 89 return 0; 90 } 91 92 static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 93 { 94 unsigned long err; 95 if (!ev_press) { 96 if (filp->f_flags & O_NONBLOCK) 97 return -EAGAIN; 98 else 99 wait_event_interruptible(button_waitq, ev_press); 100 } 101 ev_press = 0; 102 err = copy_to_user(buff, &key_value, sizeof(key_value)); 103 return sizeof(key_value); 104 } 105 //轮询poll要做两件事:poll_wait指明使用的等待队列;返回掩码 106 static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait) 107 { 108 unsigned int mask = 0; 109 poll_wait(file, &button_waitq, wait); 110 if (ev_press){ 111 mask |= POLLIN | POLLRDNORM; /*按下evpress为1,有数据刻可读*/ 112 } 113 return mask; 114 } 115 116 117 118 static struct file_operations mini2440buttons_fops = { 119 .owner = THIS_MODULE, 120 .open = s3c24xx_buttons_open, 121 .release = s3c24xx_buttons_close, 122 .read = s3c24xx_buttons_read, 123 .poll = s3c24xx_buttons_poll, 124 }; 125 126 static struct miscdevice mini2440_miscdev = { 127 128 .minor = MISC_DYNAMIC_MINOR, 129 .name ="buttons", 130 .fops = &mini2440buttons_fops, 131 }; 132 133 /* device interface */ 134 static int mini2440_buttons_probe(struct platform_device *pdev) 135 { 136 struct resource *res; 137 struct device *dev; 138 int ret; 139 int size; 140 int i; 141 142 printk("probe:%s\n", __func__); 143 dev = &pdev->dev; 144 buttons_dev = &pdev->dev; 145 146 /*平台资源获取*/ 147 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 148 if (res == NULL) { 149 dev_err(dev, "no memory resource specified\n"); 150 return -ENOENT; 151 } 152 153 size = (res->end - res->start) + 1; 154 buttons_mem = request_mem_region(res->start, size, pdev->name); 155 if (buttons_mem == NULL) { 156 dev_err(dev, "failed to get memory region\n"); 157 ret = -ENOENT; 158 goto err_req; 159 } 160 161 buttons_base = ioremap(res->start, size); 162 if (buttons_base == NULL) { 163 dev_err(dev, "failed to ioremap() region\n"); 164 ret = -EINVAL; 165 goto err_req; 166 } 167 printk(KERN_DEBUG"probe: mapped buttons_base=%p\n", buttons_base); 168 169 /*get irq number*/ 170 for(i=0; i<6; i++){ 171 buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i); 172 if(buttons_irq == NULL){ 173 dev_err(dev,"no irq resource specified\n"); 174 ret = -ENOENT; 175 goto err_map; 176 } 177 button_irqs[i] = buttons_irq->start; 178 //printk("button_irqs[%d]=%d\n",i,button_irqs[i]); 179 } 180 ret = misc_register(&mini2440_miscdev); //混杂设备注册 181 182 return 0; 183 184 err_map: 185 iounmap(buttons_base); 186 187 err_req: 188 release_resource(buttons_mem); 189 kfree(buttons_mem); 190 191 return ret; 192 } 193 194 static int mini2440_buttons_remove(struct platform_device *dev) 195 { 196 release_resource(buttons_mem); 197 kfree(buttons_mem); 198 buttons_mem = NULL; 199 200 iounmap(buttons_base); 201 misc_deregister(&mini2440_miscdev); 202 return 0; 203 } 204 205 /*平台驱动定义*/ 206 static struct platform_driver mini2440buttons_driver = { 207 .probe = mini2440_buttons_probe, 208 .remove = mini2440_buttons_remove, 209 .driver = { 210 .owner = THIS_MODULE, 211 .name = "mini2440-buttons", 212 }, 213 }; 214 215 static char banner[] __initdata = 216 "Mini2440 Buttons Driver\n"; 217 /* 218 宏 __init作用与 __initdata作用: 219 此宏定义可知标记后的函数或数据其实是放到了特定的(代码或数据)段中。 220 标记为初始化的函数,表明该函数供在初始化期间使用。在模块装载之后, 221 模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。 222 */ 223 static int __init buttons_init(void) 224 { 225 printk(banner); 226 /*平台驱动注册*/ 227 platform_driver_register(&mini2440buttons_driver); 228 return 0; 229 } 230 231 static void __exit buttons_exit(void) 232 { 233 platform_driver_unregister(&mini2440buttons_driver); 234 } 235 236 module_init(buttons_init); 237 module_exit(buttons_exit); 238 239 MODULE_AUTHOR("David Xie"); 240 MODULE_DESCRIPTION("Mini2440 Buttons Driver"); 241 MODULE_LICENSE("GPL");