喷泉之所以漂亮,是因为她有压力;瀑布之所以壮观,是因为她没退路。
全部博文(149)
分类: 嵌入式
2015-05-25 09:13:10
文章摘要:程序主要是应用了一些延时技术和中断,四个按键对应四个中断,并且每个按键按下会改变其对应的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
#include
#include
#include
#include
/*#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include "IO_irq.h" /* local definitions */
#include
#include
#include
#include
#include
#include
#include
/*
* 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");