文章摘要:程序主要是应用了一些延时技术和中断,四个按键对应四个中断,并且每个按键按下会改变其对应的led灯的状态。
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方 1.本文所介绍的程序平台 开发板:arm9-mini2440 虚拟机为:Red Hat Enterprise Linux 5 开发板上系统内核版本:linux-2.6.32.2
中断 为什么需要中断? 1.外设的处理速度一般慢于CPU 2.CPU不能一直等待外部事件 所以设备必须有一种方法来通知CPU它的工作进度,这种方法就是中断。
中断实现 在Linux驱动程序中,为设备实现一个中断包含两个步骤: 1.向内核注册中断 2.实现中断处理函数
中断注册 request_irq用于实现中断的注册功能: 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 与中断 理有关的各种选项。 const char * devname 设备名 void *dev_id 共享中断时使用,在共享中断的时候必须唯一
中断注册(中断标志) 在flags参数中,可以选择一些与中断 理有关 的选项,如: IRQF_DISABLED(SA_INTERRUPT) 如果设置该位,表示是一个"快速"中断处理程序;如果没有设置这位,那是一个"慢速"中断处理程序。 IRQF_SHARED(SA_SHIRQ) 该位表明中断可以在设备间共享。
快速/慢速中断
这两种类型的中断处理程序的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是"开启中断"标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型 的中断打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。
共享中断 共享中断就是将不同的设备挂到 同一个中断信号线上。Linux对共享的支持主要是为PCI设备服务。 共享中断也是通过request_irq函数来注册的, 但有三个特别之处: 1. 申请共享中断时,必须在flags参数中指定 IRQF_SHARED位 2. dev_id参数必须是唯一的。 3.共享中断的处理程序中,不能使用 disable_irq(unsigned int irq) 如果使用了这个函数,共享中断信号线的其它设备将同样无法使用中断,也就无法正常工作了。
中断处理程序 什么是中断处理程序,有何特别之处? 中断处理程序就是普通的C代码。特别之处在于中断处理程序是在中断上下文中运行的,它的行为受到某些限制: 1.不能向用户空间发送或接受数据 2.不能使用可能引起阻塞的函数 Linux设备驱动阻塞与非阻塞阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。非阻塞操作的进程在不能进行设备操作时,并不挂起。被挂起的进程进入sleep状态,被从调度器的运行队列移走,直到等待的条件被满足。在Linux驱动程序中,我们可以使用等待队列(wait queue)来实现阻塞操作。wait queue很早就作为一个基本的功能单位出现在Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。
3.不能使用可能引起调度的函数 调度(schedulers)是内核的主要职责之一,就是决定该轮到哪个任务运行了。多数实时内核是基于优先级调度法的。每个任务根据重要程度的不同,被赋予一定的优先级。基于优先级的调度法是指,CPU总是让处于就绪态的,优先级最高的任务先运行。基于优先级的内核有2种类型:不可剥夺型和可剥夺型。
中断处理函数流程 void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /* 判断是否是本设备产生了中断(为什 要做这样的检测?) */ value = inb(short_base); if (!(value & 0x80)) return;
/* 清除中断位(如果设备支持自动清除,则不需要这步) */ outb(value & 0x7F, short_base);
/* 中断处理,通常是数据接收 */ 。。。。。。。。。
/* 唤醒等待数据的进程 */ wake_up_interruptible(&short_queue); }
释放中断 void free_irq(unsigned int irq, void *dev_id)
2.程序清单 本次实验程序为tekkamanninja博客《LDD3》代码,本人作了改动和较为详细的注释,如有错误请指出。 注意:在本次实验中发现几个问题还没解决? 如果我使用的是以下的4个按键中断 分别对应的是按键1,2,3,4, #define IO_IRQ1 IRQ_EINT8 #define IO_IRQ2 IRQ_EINT11 #define IO_IRQ3 IRQ_EINT13 #define IO_IRQ4 IRQ_EINT14 会在下面这段代码设置之后 s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq1, button_irqs[0].pin_setting); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq2, button_irqs[1].pin_setting); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq3, button_irqs[2].pin_setting); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq4, button_irqs[3].pin_setting); 改变led灯的亮灭状态,具体是造成1,2灯熄灭,并且不能设置其亮灭状态,并且导致蜂鸣器开启,其他两个灯可以设置其亮灭状态,如果添加了下面这段代码后 for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i], 0); } 可以改变4个led灯的亮灭状态,但是还是不能阻止蜂鸣器开启, 若是用以下的4个按键中断 分别对应的是按键5,6,3,4, #define IO_IRQ1 IRQ_EINT15 #define IO_IRQ2 IRQ_EINT19 #define IO_IRQ3 IRQ_EINT13 #define IO_IRQ4 IRQ_EINT14 可以阻止蜂鸣器的开启,但是如果不重新设置 for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i], 0); } 还是不能控制2个led灯的亮灭,如果重新设置led灯,可以控制全部的led灯的亮灭,但是在退出测试程序的时候会出错,有点类似于程序无法关闭,退出之后按键仍然可以控制led灯。 IO_irq.h #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */ #include <asm/io.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/irq.h> /*#include "hardware.h"
#include "irqs.h" #include "regs-gpioj.h" #include "regs-gpio.h"*/
#if 0 #define IO_IRQ1 IRQ_EINT8 #define IO_IRQ2 IRQ_EINT11 #define IO_IRQ3 IRQ_EINT13 #define IO_IRQ4 IRQ_EINT14 #endif
#if 1 #define IO_IRQ1 IRQ_EINT15 #define IO_IRQ2 IRQ_EINT19 #define IO_IRQ3 IRQ_EINT13 #define IO_IRQ4 IRQ_EINT14 #endif
#define IO_IRQ1_MASK 0x1 #define IO_IRQ2_MASK 0x2 #define IO_IRQ3_MASK 0x4 #define IO_IRQ4_MASK 0x8 #define IO_IRQ_ALL_MASK 0x10
#define DELAY 10
#define UP 2 #define DOWN 4 #define UNKNOWN 8
struct IO_irq_dev { struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ unsigned int IO_irq1; unsigned int IO_irq2; unsigned int IO_irq3; unsigned int IO_irq4; unsigned char IO_status; };
struct IO_irq_key { unsigned long prevjiffies ; int count; int status; };
/* * Split minors in two parts */ #define TYPE(minor) (((minor) >> 4) & 0xf) /* high nibble */ #define NUM(minor) ((minor) & 0xf) /* low nibble */
/* * The different configurable parameters */ extern int IO_irq_major; /* main.c */ extern int IO_irq_minor;
int IO_irq_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
/* * Ioctl definitions */
/* Use 'k' as magic number */ #define IO_IRQ_MAGIC 'k' /* Please use a different 8-bit number in your code */
#define IO_IRQ_1 _IO(IO_IRQ_MAGIC, 0) #define IO_IRQ_2 _IO(IO_IRQ_MAGIC, 1) #define IO_IRQ_3 _IO(IO_IRQ_MAGIC, 2) #define IO_IRQ_4 _IO(IO_IRQ_MAGIC, 3) #define IO_IRQ_ALL _IO(IO_IRQ_MAGIC, 4) #define IO_KFIFO_SIZE _IO(IO_IRQ_MAGIC, 5) #define IO_KFIFO_RESET _IO(IO_IRQ_MAGIC, 6) #define IO_IRQ_MAXNR 6
IO_irq.c #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h>
#include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/seq_file.h> #include <linux/cdev.h> #include <linux/kfifo.h> #include <linux/sched.h> /* current and everything */ //#include <linux/delay.h> /* udelay */
#include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_*_user */ #include <asm/atomic.h>
#include "IO_irq.h" /* local definitions */
#include <linux/mm.h>
#include <linux/device.h>//自动创建
#include <linux/delay.h>//ssleep
#include <asm/unistd.h>
#include <mach/regs-gpio.h>/*端口定义*/ #include <mach/hardware.h>
#include <linux/gpio.h>/*s3c2410_gpio_cfgpin等函数*/
/* * Our parameters which can be set at load time. */ int IO_irq_major = 0; int IO_irq_minor = 3;
module_param(IO_irq_major, int, S_IRUGO); module_param(IO_irq_minor, int, S_IRUGO);
#define DeviceName "button_leds"
struct IO_irq_dev *IO_irq_devices; /* allocated in scull_init_module */ static atomic_t IO_irq_available = ATOMIC_INIT(1); static spinlock_t IO_irq_lock = SPIN_LOCK_UNLOCKED; static DECLARE_WAIT_QUEUE_HEAD(IO_irq_wait); static unsigned long prevjiffies = 0; static unsigned int prevkey = 0; static struct IO_irq_key mykey[4]; struct workqueue_struct *tekkamanwork; static struct delayed_work irq_work_delay; static struct work_struct irq_work; static struct tasklet_struct keytask; struct kfifo *tekkamanfifo; static spinlock_t tekkamanlock= SPIN_LOCK_UNLOCKED; static unsigned char *tekkamantmp; static unsigned char *tekkamanbuf ;
//消抖 #define KEY_TIMER_DELAY1 (HZ/50) //按键按下去抖延时20毫秒 #define KEY_TIMER_DELAY2 (HZ/10) //按键抬起去抖延时100毫秒 static struct timer_list key_timers[4]; //定义4个按键去抖动定时器!!! static int irq_flag = 0;//中断标志
//两个即将在、/dev 下创建的设备名 //自动创建设备文件 static struct class * myclass; static struct device * mydev;
struct button_irq_desc {
int irq; int pin; int pin_setting; int number; char *name;
};
/* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"}, /* K1 */
{IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 1, "KEY2"}, /* K2 */
{IRQ_EINT13, S3C2410_GPG(5), S3C2410_GPG5_EINT13, 2, "IO_irq3"}, /* K3 */
{IRQ_EINT14, S3C2410_GPG(6), S3C2410_GPG6_EINT14, 3, "IO_irq4"}, /* K4 */
{IRQ_EINT15, S3C2410_GPG(7), S3C2410_GPG7_EINT15, 4, "KEY5"}, /* K5 */
{IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY6"}, /* K6 */
};
static unsigned long led_table [] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPB(7), S3C2410_GPB(8), };
/* GPBX的输出状态 */
static unsigned int led_cfg_table [] = { S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, };
/*key1_tasklet*/ void key1_tasklet(unsigned long arg) { struct IO_irq_key *data = (struct IO_irq_key *)arg; unsigned long j=0;
printk("<5> tasklet \n");
printk("\n**************key1_tasklet_start*****************\n"); j = jiffies; //in_interrupt() 是判断当前进程是否处于中断上下文 //,这个中断上下文包括底半部和硬件中断处理过程 printk("time:%08lx delta:%3li inirq:%i pid:%3i cpu:%i command:%s\n", j, j - data->prevjiffies, in_interrupt() ? 1 : 0, current->pid, smp_processor_id(), current->comm); printk("\n**************key1_tasklet_end*****************\n");
} /*key2-3_work_fn*/ void irq_work_delay_fn(void *arg) { struct IO_irq_key *data = &mykey[1]; unsigned long j=0;
printk("<5> work delay \n");
printk("\n**************key2_workqueue_start*****************\n"); j = jiffies; printk("time:%08lx delta:%3li inirq:%i pid:%3i cpu:%i command:%s\n", j, j - data->prevjiffies, in_interrupt() ? 1 : 0, current->pid, smp_processor_id(), current->comm); printk("\n**************key2_workqueu_end*****************\n");
}
void irq_work_fn(void *arg) { struct IO_irq_key *data = &mykey[2]; unsigned long j=0;
printk("<5> work \n");
printk("\n**************key3_workqueue_start*****************\n"); j = jiffies; printk("time:%08lx delta:%3li inirq:%i pid:%3i cpu:%i command:%s\n", j, j - data->prevjiffies, in_interrupt() ? 1 : 0, current->pid, smp_processor_id(), current->comm); printk("\n**************key3_workqueu_end*****************\n");
} /* * irq handled */ static void leds_set(int i) { unsigned int pin_seting;
int j ; for( j = 0; j < 4; j++) printk("<5>led %d : %d",j+1, s3c2410_gpio_getpin(led_table[j]));
pin_seting = s3c2410_gpio_getpin(led_table[i-1]); if(pin_seting) { s3c2410_gpio_setpin(led_table[i-1], 0); printk("<6> led %d on \n", i); } else { s3c2410_gpio_setpin(led_table[i-1], 1); printk("<6> led %d off \n", i);
} for( j = 0; j < 4; j++) printk("<5>led %d : %d",j+1, s3c2410_gpio_getpin(led_table[j])); printk("<5> leds set \n");
}
static void key_is_status() { int i; unsigned long j =0; j = jiffies; int result = 0; unsigned int len = 0;
printk("<5> status \n");
for(i =0 ; i< 4; i++) if(mykey[i].status == DOWN) break; if(i >= 4) { irq_flag = 0; return ; } else if (i == 3) { if (!prevkey) len = sprintf(tekkamanbuf,"\n**********KEY = 4**********\nNO prevkey\nnow jiffies =0x%08lx\ncount = %d\n*********KEY = 4 END***********\n", j , ++mykey[i].count); else if (!mykey[i].prevjiffies) len = sprintf(tekkamanbuf,"\n**********KEY = 4**********\nprevkey=%d at 0x%08lx\nNO prekey4\nnow jiffies =0x%08lx\ncount = %d\n*********KEY = 4 END***********\n" , prevkey , prevjiffies , j, ++mykey[i].count); else len = sprintf(tekkamanbuf,"\n**********KEY = 4**********\nprevkey=%d at 0x%08lx\nprekey4 at 0x%08lx\nnow jiffies =0x%08lx\ncount = %d\n*********KEY = 4 END***********\n" , prevkey , prevjiffies , mykey[i].prevjiffies , j, ++mykey[i].count);
} else { printk("\n**************KEY = %d*****************\n", i+1); if (!prevkey) printk("NO prevkey\nnow jiffies =0x%08lx\ncount = %d\n", j , ++mykey[i].count); else if (!mykey[i].prevjiffies) printk("prevkey=%d at 0x%08lx\nNO prekey1\nnow jiffies =0x%08lx\ncount = %d\n" , prevkey , prevjiffies , j, ++mykey[i].count); else printk("prevkey=%d at 0x%08lx\nprekey1 at 0x%08lx\nnow jiffies =0x%08lx\ncount = %d\n" , prevkey , prevjiffies , mykey[i].prevjiffies , j, ++mykey[i].count); }
prevkey = 1; prevjiffies = mykey[i].prevjiffies =j;///将先前保存的时间值给KEY switch(i) { case 0: /*void tasklet_schedule(struct tasklet_struct *t); 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行 ; 这保证了在其他事件被处理当中发生的事件受到应有的注意 . 这个做法也允许一个 tasklet 重新调度它自己 */ tasklet_schedule(&keytask);//!??????????? /*tasklet_init(&keytask , key1_tasklet , (unsigned long)&key1); 调度这个函数void key1_tasklet(unsigned long arg)*/
leds_set(1); break;
case 1:
/*int queue_delayed_work(struct workqueue_struct *queue, struct delayed_work *work, unsigned long delay); 每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies 才会被执行 。这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入 */ /*struct workqueue_struct *create_workqueue(const char *name);*/ //tekkamanwork = create_workqueue("tekkamanwork");
/*INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *)); */ //INIT_DELAYED_WORK(&irq_work_delay, irq_work_delay_fn); /* //queue_delayed_work()添加工作到新建tekkamanwork的工作队列中去 //这里添加的是queue_delay_work类型的irq_work_delay工作是指至少 //要经过DELAY那么多的时间才能执行*/ if ((result =queue_delayed_work(tekkamanwork , &irq_work_delay, DELAY))!=1) printk("IO_irq_interrupt2 cannot add work ! ");
printk("result = %d ",result);
leds_set(2); break;
case 2: /*提交工作int schedule_work(&jiq_work);对于work_struct结构*/ /*INIT_WORK(struct work_struct *work, void (*function)(void *));*/ //INIT_WORK(&irq_work, irq_work_fn); if ((result = schedule_work(&irq_work))!=1) printk("IO_irq_interrupt3 cannot add work sharequeue ! "); printk("result = %d ",result);
leds_set(3); break;
case 3: //将tekkamanbuf里面的内容给缓冲区tekkamanfifo // 向缓冲区里写入数据 kfifo_put(tekkamanfifo, tekkamanbuf, len);
leds_set(4); break;
default: return ;
}
printk("\n**************KEY = %d END*****************\n", i+1); irq_flag = 0;
}
//消抖 static void buttons_timer(unsigned long arg) { //获取当前按键资源的索引 int key = arg; printk("<5> timer \n"); //获取当前按键引脚上的电平值来判断按键是按下还是抬起 int up = s3c2410_gpio_getpin(button_irqs[key].pin);
if(!up)//低电平,按键按下 { if(mykey[key].status == UNKNOWN) { mykey[key].status = DOWN; irq_flag = 1; key_is_status();
} //设置当前按键抬起去抖定时器的延时并启动定时器 key_timers[key].expires = jiffies + KEY_TIMER_DELAY2; add_timer(&key_timers[key]);
} else//抖动 { mykey[key].status = UP; } }
static irqreturn_t IO_irq_interrupt1(int irq, void *dev_id, struct pt_regs *regs) {
//设置当前按键按下去抖定时器的延时并启动定时器 //就是如果我在驱动里调用了add_timer()后,驱动是继续执行自己的任务, //,,直到add_timer指定的时间到达后,才会去执行指定的函数 //HZ是一个值,内核会根据这个值把1秒分成HZ个小段,这每个小段叫做tick,add_timer()是以tick为单位的, //因此add_timer()指定HZ为参数,就是延迟HZ个tick,因此是1秒。 printk("<5> interrupt1 \n"); if(mykey[0].status== UP) { //设置当前按键的状态为不确定 mykey[0].status = UNKNOWN;
//设置当前按键按下去抖定时器的延时并启动定时器 key_timers[0].expires = jiffies + KEY_TIMER_DELAY1; add_timer(&key_timers[0]); }
return IRQ_RETVAL(IRQ_HANDLED); //return IRQ_HANDLED;//区别??? }
static irqreturn_t IO_irq_interrupt2(int irq, void *dev_id, struct pt_regs *regs) {
printk("<5> interrupt2 \n"); //设置当前按键按下去抖定时器的延时并启动定时器 if(mykey[1].status== UP) { //设置当前按键的状态为不确定 mykey[1].status = UNKNOWN;
//设置当前按键按下去抖定时器的延时并启动定时器 key_timers[1].expires = jiffies + KEY_TIMER_DELAY1; add_timer(&key_timers[1]); }
return IRQ_HANDLED; } static irqreturn_t IO_irq_interrupt3(int irq, void *dev_id, struct pt_regs *regs) {
printk("<5> interrupt3 \n"); //设置当前按键按下去抖定时器的延时并启动定时器 if(mykey[2].status== UP) { //设置当前按键的状态为不确定 mykey[2].status = UNKNOWN;
//设置当前按键按下去抖定时器的延时并启动定时器 key_timers[2].expires = jiffies + KEY_TIMER_DELAY1; add_timer(&key_timers[2]); } return IRQ_HANDLED; } static irqreturn_t IO_irq_interrupt4(int irq, void *dev_id, struct pt_regs *regs) {
printk("<5> interrupt4 \n"); //设置当前按键按下去抖定时器的延时并启动定时器 if(mykey[3].status== UP) { //设置当前按键的状态为不确定 mykey[3].status = UNKNOWN;
//设置当前按键按下去抖定时器的延时并启动定时器 key_timers[3].expires = jiffies + KEY_TIMER_DELAY1; add_timer(&key_timers[3]); }
return IRQ_HANDLED; }
/* * Open and close */
int IO_irq_open(struct inode *inode, struct file *filp) { struct IO_irq_dev *dev; /* device information */ int result1,result2,result3,result4; int i;
for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2);
spin_lock(&IO_irq_lock);
while (! atomic_dec_and_test (&IO_irq_available)) { atomic_inc(&IO_irq_available); spin_unlock(&IO_irq_lock); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible (IO_irq_wait, atomic_read (&IO_irq_available))) return -ERESTARTSYS; /* tell the fs layer to handle it */
spin_lock(&IO_irq_lock); }
spin_unlock(&IO_irq_lock);
dev = container_of(inode->i_cdev, struct IO_irq_dev, cdev);
for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2); //IRQ_EINT0中断0 if ((dev->IO_irq1 >= IRQ_EINT0)&&(dev->IO_irq2 >= IRQ_EINT0)&&(dev->IO_irq3 >= IRQ_EINT0)&&(dev->IO_irq4 >= IRQ_EINT0)) { /*int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id); request_irq 的返回值: 0 指示成功,或返回一个负的错误码,如 -EBUSY 表示另一个驱动已经占用了你所请求的中断线。 函数的参数如下: unsigned int irq :请求的中断号 irqreturn_t (*handler) :安装的处理函数指针。 unsigned long flags :一个与中断管理相关的位掩码选项。 const char *dev_name :传递给 request_irq 的字符串,用来在 /proc/interrupts 来显示中断的拥有者。 void *dev_id :用于共享中断信号线的指针。它是唯一的标识,在中断线空闲时可以使用它 ,驱动程序也可以用它来指向自己的私有数据区(来标识哪个设备产生中断)。若中断没有被共享 ,dev_id 可以设置为 NULL,但推荐用它指向设备的数据结构。 flags 中可以设置的位如下: SA_INTERRUPT :快速中断标志。快速中断处理例程运行在当前处理器禁止中断的状态下。 SA_SHIRQ : 在设备间共享中断标志。 SA_SAMPLE_RANDOM :该位表示产生的中断能对 /dev/random 和 /dev/urandom 使用的熵池(entropy pool)有贡献 。读取这些设备会返回真正的随机数,从而有助于应用程序软件选择用于加密的安全密钥。 若设备以真正随机的周期产生中断 ,就应当设置这个标志。若设备中断是可预测的,这个标志不值得设置。可能被攻击者影响的设备不应当设置这个标志 。更多信息看 drivers/char/random.c 的注释。
*/ for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2);
result1 = request_irq(dev->IO_irq1, IO_irq_interrupt1, 0 , "IO_irq1", (void *)1); result2 = request_irq(dev->IO_irq2, IO_irq_interrupt2, 0 , "IO_irq2", (void *)2); result3 = request_irq(dev->IO_irq3, IO_irq_interrupt3, 0 , "IO_irq3", (void *)3); result4 = request_irq(dev->IO_irq4, IO_irq_interrupt4, 0 , "IO_irq4", (void *)4); if (result1 || result2 || result3 || result4 ) { printk( "IO_irq: can't get assigned one of irq \n"); if (!result1) free_irq(IO_irq_devices->IO_irq1, (void *)1); if (!result2) free_irq(IO_irq_devices->IO_irq2, (void *)2); if (!result3) free_irq(IO_irq_devices->IO_irq3, (void *)3); if (!result4) free_irq(IO_irq_devices->IO_irq4, (void *)4); return -EAGAIN; } else {
for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2);
#if 0 /*作用:设置相应GPIO口的上拉电阻, 如pin=S3C2410_GPB5 to=0 则:设置S3C2410_GPB5的不要上拉电阻 如pin=S3C2410_GPB5 to=1 则:设置S3C2410_GPB5的要上拉电阻 */ s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq1, button_irqs[0].pin_setting);///设置为中断状态多功能状态?? //s3c2410_gpio_pullup(button_irqs[0].irq, 0);//手否要上拉电阻 ???上拉电阻有什作用//?/ //上拉电阻让电平值稳定在一个值??? s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq2, button_irqs[1].pin_setting); //s3c2410_gpio_pullup(button_irqs[1].irq, 1); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq3, button_irqs[2].pin_setting); //s3c2410_gpio_pullup(button_irqs[2].irq, 1); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq4, button_irqs[3].pin_setting); //s3c2410_gpio_pullup(button_irqs[3].irq, 1);
for (i = 0; i < 4; i++) { /*设置GPIO对应的配置寄存器GPIOCON为输出状态*/ s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
/*设置GPIO对应的数据寄存器GPIODAT为低电平 *在模块加载结束后,四个LED应该是全部都是全liang*/ s3c2410_gpio_setpin(led_table[i], 0); } #endif
#if 1 /*作用:设置相应GPIO口的上拉电阻, 如pin=S3C2410_GPB5 to=0 则:设置S3C2410_GPB5的不要上拉电阻 如pin=S3C2410_GPB5 to=1 则:设置S3C2410_GPB5的要上拉电阻 */ s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq1, button_irqs[4].pin_setting);///设置为中断状态多功能状态?? //s3c2410_gpio_pullup(button_irqs[0].irq, 0);//手否要上拉电阻 ???上拉电阻有什作用//?/ //上拉电阻让电平值稳定在一个值??? s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq2, button_irqs[5].pin_setting); //s3c2410_gpio_pullup(button_irqs[1].irq, 1); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq3, button_irqs[2].pin_setting); //s3c2410_gpio_pullup(button_irqs[2].irq, 1); s3c2410_gpio_cfgpin(IO_irq_devices->IO_irq4, button_irqs[3].pin_setting); //s3c2410_gpio_pullup(button_irqs[3].irq, 1);
for (i = 0; i < 4; i++) { /*设置GPIO对应的配置寄存器GPIOCON为输出状态*/ s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
/*设置GPIO对应的数据寄存器GPIODAT为低电平 *在模块加载结束后,四个LED应该是全部都是全liang*/ s3c2410_gpio_setpin(led_table[i], 0); } #endif dev->IO_status = 0x1f ;//状态011111B 四个中断全开,第五位表示全部中断 for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2); #if 1 set_irq_type(button_irqs[4].irq,IRQ_TYPE_EDGE_FALLING );//设置中断类型; set_irq_type(button_irqs[5].irq,IRQ_TYPE_EDGE_FALLING );//下降沿IRQ_TYPE_EDGE_RISING set_irq_type(button_irqs[2].irq,IRQ_TYPE_EDGE_FALLING ); set_irq_type(button_irqs[3].irq,IRQ_TYPE_EDGE_FALLING );//下降沿IRQ_TYPE_EDGE_FALLING #endif
#if 0 set_irq_type(button_irqs[0].irq,IRQ_TYPE_EDGE_FALLING );//设置中断类型; set_irq_type(button_irqs[1].irq,IRQ_TYPE_EDGE_FALLING );//下降沿IRQ_TYPE_EDGE_RISING set_irq_type(button_irqs[2].irq,IRQ_TYPE_EDGE_FALLING ); set_irq_type(button_irqs[3].irq,IRQ_TYPE_EDGE_FALLING );//下降沿IRQ_TYPE_EDGE_FALLING #endif for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); ssleep(2); } } else { printk("IO_irq: get IRQ failed !\n"); return -EAGAIN; }
filp->private_data = dev; /* for other methods */
for(i = 0; i < 4; i++) //初始化并设置4个去抖定时器 setup_timer(&key_timers[i], buttons_timer, i);
for(i = 0; i < 4; i++) mykey[i].status = UP;
for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i]));
/*void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); */ tasklet_init(&keytask , key1_tasklet , (unsigned long)&mykey[0]);
/*struct workqueue_struct *create_workqueue(const char *name);*/ tekkamanwork = create_workqueue("tekkamanwork");
/*INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *)); */ INIT_DELAYED_WORK(&irq_work_delay, irq_work_delay_fn);
/*INIT_WORK(struct work_struct *work, void (*function)(void *));*/ INIT_WORK(&irq_work, irq_work_fn);
tekkamantmp = kmalloc(512, GFP_KERNEL); tekkamanbuf = kmalloc (512 , GFP_KERNEL);
/*struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock); 调用kfifo_alloc不必保证size是2的幂,它内部会把size向上调整到2的整数次幂 。空间分配的内部实现使用kmalloc。函数内部调用kfifo_init/ */ tekkamanfifo = kfifo_alloc(512, GFP_KERNEL, &tekkamanlock);
printk( "IO_irq: opened ! \n"); for(i = 0; i < 4; i++) printk("<5>led %d : %d",i +1, s3c2410_gpio_getpin(led_table[i])); return nonseekable_open(inode, filp); /* success */ }
int IO_irq_release(struct inode *inode, struct file *filp) { int i;
free_irq(IO_irq_devices->IO_irq1, NULL); free_irq(IO_irq_devices->IO_irq2, NULL); free_irq(IO_irq_devices->IO_irq3, NULL); free_irq(IO_irq_devices->IO_irq4, NULL);
for(i =0; i < 4; i++) del_timer(&key_timers[i]);
/* s3c2410_gpio_cfgpin(S3C2410_GPG(11), S3C2410_GPG11_INP);// s3c2410_gpio_cfgpin(S3C2410_GPG(3), S3C2410_GPG3_INP); s3c2410_gpio_cfgpin(S3C2410_GPF(2), S3C2410_GPF2_INP); s3c2410_gpio_cfgpin(S3C2410_GPF(0), S3C2410_GPF0_INP); */ s3c2410_gpio_cfgpin(button_irqs[0].irq, S3C2410_GPIO_INPUT);// s3c2410_gpio_cfgpin(button_irqs[1].irq, S3C2410_GPIO_INPUT); s3c2410_gpio_cfgpin(button_irqs[2].irq, S3C2410_GPIO_INPUT); s3c2410_gpio_cfgpin(button_irqs[3].irq, S3C2410_GPIO_INPUT);
IO_irq_devices->IO_status = 0 ;
tasklet_kill(&keytask);
if(!cancel_delayed_work(&irq_work_delay)) flush_workqueue(tekkamanwork); destroy_workqueue(tekkamanwork);
kfifo_free(tekkamanfifo); kfree(tekkamantmp); kfree(tekkamanbuf); atomic_inc(&IO_irq_available); /* release the device */ wake_up_interruptible_sync(&IO_irq_wait); /* awake other uid's */ printk( "IO_irq: release ! \n");
return 0; }
/* * The ioctl() implementation */
ssize_t IO_irq_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct IO_irq_dev *dev = filp->private_data; ssize_t retval = 0;
if (down_interruptible(&dev->sem)) return -ERESTARTSYS;
if (count > kfifo_len(tekkamanfifo)) count = kfifo_len(tekkamanfifo); ///从缓冲区里读出数据到tekkamantmp count = kfifo_get(tekkamanfifo,tekkamantmp, count);
if (copy_to_user(buf, tekkamantmp, count)) { retval = -EFAULT; goto out; } retval = count;
out: up(&dev->sem); return retval; }
#if 11 int IO_irq_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) {
//int err = 0; int retval = 0; //unsigned int current_status; /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != IO_IRQ_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > IO_IRQ_MAXNR) return -ENOTTY;
/* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type' is user-oriented, while * access_ok is kernel-oriented, so the concept of "read" and * "write" is reversed */ switch(cmd) { case IO_IRQ_1: //IO_IRQ1_MASK 0x01测试第一个中断是否开 if (IO_irq_devices->IO_status&IO_IRQ1_MASK) { disable_irq(IO_IRQ1); IO_irq_devices->IO_status &= ~IO_IRQ1_MASK; printk(KERN_NOTICE "disable_irq IO_IRQ_1 !\n"); } else { enable_irq(IO_IRQ1); IO_irq_devices->IO_status |= IO_IRQ1_MASK; printk(KERN_NOTICE "enable_irq IO_IRQ_1 !\n"); } break;
case IO_IRQ_2: /* Set: arg points to the value */ if (IO_irq_devices->IO_status&IO_IRQ2_MASK) { disable_irq(IO_IRQ2); IO_irq_devices->IO_status &= ~IO_IRQ2_MASK; printk(KERN_NOTICE "disable_irq IO_IRQ_2 !\n"); } else { enable_irq(IO_IRQ2); IO_irq_devices->IO_status |= IO_IRQ2_MASK; printk(KERN_NOTICE "enable_irq IO_IRQ_2 !\n"); } break;
case IO_IRQ_3: /* Tell: arg is the value */ if (IO_irq_devices->IO_status&IO_IRQ3_MASK) { disable_irq(IO_IRQ3); IO_irq_devices->IO_status &= ~IO_IRQ3_MASK; printk(KERN_NOTICE "disable_irq IO_IRQ_3 !\n"); } else { enable_irq(IO_IRQ3); IO_irq_devices->IO_status |= IO_IRQ3_MASK; printk(KERN_NOTICE "enable_irq IO_IRQ_3 !\n"); } break;
case IO_IRQ_4: /* Get: arg is pointer to result */ if (IO_irq_devices->IO_status&IO_IRQ4_MASK) { disable_irq(IO_IRQ4); IO_irq_devices->IO_status &= ~IO_IRQ4_MASK; printk(KERN_NOTICE "disable_irq IO_IRQ_4 !\n"); } else { enable_irq(IO_IRQ4); IO_irq_devices->IO_status |= IO_IRQ4_MASK; printk(KERN_NOTICE "enable_irq IO_IRQ_4 !\n"); } break;
case IO_IRQ_ALL: /* eXchange: use arg as pointer */ //IO_IRQ_ALL 0x10测试第五位判断中断是否全开 if (IO_irq_devices->IO_status&IO_IRQ_ALL_MASK) { if (IO_irq_devices->IO_status&IO_IRQ1_MASK) disable_irq(IO_IRQ1); if (IO_irq_devices->IO_status&IO_IRQ2_MASK) disable_irq(IO_IRQ2); if (IO_irq_devices->IO_status&IO_IRQ3_MASK) disable_irq(IO_IRQ3); if (IO_irq_devices->IO_status&IO_IRQ4_MASK) disable_irq(IO_IRQ4); IO_irq_devices->IO_status &= ~IO_IRQ_ALL_MASK; printk(KERN_NOTICE "disable_irq IO_IRQ_ALL !\n"); } else { if (IO_irq_devices->IO_status&IO_IRQ1_MASK) enable_irq(IO_IRQ1); if (IO_irq_devices->IO_status&IO_IRQ2_MASK) enable_irq(IO_IRQ2); if (IO_irq_devices->IO_status&IO_IRQ3_MASK) enable_irq(IO_IRQ3); if (IO_irq_devices->IO_status&IO_IRQ4_MASK) enable_irq(IO_IRQ4); IO_irq_devices->IO_status |= IO_IRQ_ALL_MASK; printk(KERN_NOTICE "enable_irq IO_IRQ_ALL !\n"); } break; case IO_KFIFO_SIZE: return (int) kfifo_len(tekkamanfifo);
case IO_KFIFO_RESET: kfifo_reset(tekkamanfifo);//???这里设置有用吗 ?都已经分配了大小了//?? break;
default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; }
printk(KERN_NOTICE "IO_status= %x !\n",IO_irq_devices->IO_status); return retval;
} #endif
struct file_operations IO_irq_fops = { .owner = THIS_MODULE, .ioctl = IO_irq_ioctl, .open = IO_irq_open, .release = IO_irq_release, .llseek = no_llseek, .read = IO_irq_read,
};
/* * Finally, the module stuff */
/* * The cleanup function is used to handle initialization failures as well. * Thefore, it must be careful to work correctly even if some of the items * have not been initialized */ void IO_irq_cleanup_module(void) { dev_t devno = MKDEV(IO_irq_major, 0);
//卸载模块时候也要同事把/dev/下的设备名卸载掉 device_unregister(mydev); //卸载掉dev中对应的设备文件 class_destroy(myclass); //卸载设备队对应的class
/* Get rid of our char dev entries */ if (IO_irq_devices) { cdev_del(&IO_irq_devices->cdev); kfree(IO_irq_devices); }
/* cleanup_module is never called if registering failed */ unregister_chrdev_region(devno, IO_irq_minor);
}
/* * Set up the char_dev structure for this device. */ static void IO_irq_setup_cdev(struct IO_irq_dev *dev) { int err, devno = MKDEV(IO_irq_major, 0);
cdev_init(&dev->cdev, &IO_irq_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add (&dev->cdev, devno, IO_irq_minor); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding IO_irq", err); }
int IO_irq_init_module(void) { int result; dev_t dev = 0; int i; myclass = class_create(THIS_MODULE, "my_device_driver");
/* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if (IO_irq_major) { dev = MKDEV(IO_irq_major, 0); result = register_chrdev_region(dev, IO_irq_minor, "IO_irq"); } else { result = alloc_chrdev_region(&dev, 0, IO_irq_minor, "IO_irq"); IO_irq_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "IO_irq: can't get major %d\n", IO_irq_major); return result; }
/* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ IO_irq_devices = kmalloc(sizeof(struct IO_irq_dev), GFP_KERNEL); if (!IO_irq_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(IO_irq_devices, 0, sizeof(struct IO_irq_dev));
/* Initialize each device. */ init_MUTEX(&IO_irq_devices->sem); #if 0 IO_irq_devices->IO_irq1 = (unsigned int) button_irqs[0].irq; IO_irq_devices->IO_irq2 = (unsigned int) button_irqs[1].irq; IO_irq_devices->IO_irq3 = (unsigned int) button_irqs[2].irq; IO_irq_devices->IO_irq4 = (unsigned int) button_irqs[3].irq; #endif
#if 1 IO_irq_devices->IO_irq1 = (unsigned int) button_irqs[4].irq; IO_irq_devices->IO_irq2 = (unsigned int) button_irqs[5].irq; IO_irq_devices->IO_irq3 = (unsigned int) button_irqs[2].irq; IO_irq_devices->IO_irq4 = (unsigned int) button_irqs[3].irq; #endif
IO_irq_devices->IO_status = 0 ;//设置中断全关
IO_irq_setup_cdev(IO_irq_devices); #if 1 for (i = 0; i < 4; i++) { /*设置GPIO对应的配置寄存器GPIOCON为输出状态*/ s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
/*设置GPIO对应的数据寄存器GPIODAT为低电平 *在模块加载结束后,四个LED应该是全部都是全liang*/ s3c2410_gpio_setpin(led_table[i], 0); } #endif //自动创建设备文件 //mydev = device_create(myclass, NULL, MKDEV(IO_irq_major, 0), DeviceName); //2.6.26以上 mydev = device_create(myclass, NULL, MKDEV(IO_irq_major, 0), NULL,DeviceName);
return 0; /* succeed */
fail: IO_irq_cleanup_module(); return result; }
module_init(IO_irq_init_module); module_exit(IO_irq_cleanup_module);
MODULE_AUTHOR("Tekkaman Ninja and gongzhi modify"); MODULE_LICENSE("Dual BSD/GPL");
|
|