Chinaunix首页 | 论坛 | 博客
  • 博客访问: 227043
  • 博文数量: 149
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1127
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-26 15:53
个人简介

喷泉之所以漂亮,是因为她有压力;瀑布之所以壮观,是因为她没退路。

文章分类

全部博文(149)

文章存档

2016年(25)

2015年(124)

我的朋友

分类: 嵌入式

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_DISABLEDSA_INTERRUPT

如果设置该位,表示是一个"快速"中断处理程序;如果没有设置这位,那是一个"慢速"中断处理程序。

IRQF_SHAREDSA_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个按键中断 分别对应的是按键1234

 #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灯的亮灭状态,具体是造成12灯熄灭,并且不能设置其亮灭状态,并且导致蜂鸣器开启,其他两个灯可以设置其亮灭状态,如果添加了下面这段代码后

         for (i = 0; i < 4; i++) {

                   s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

                   s3c2410_gpio_setpin(led_table[i], 0);

         }

可以改变4led灯的亮灭状态,但是还是不能阻止蜂鸣器开启,

若是用以下的4个按键中断 分别对应的是按键5634

#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);

         }

还是不能控制2led灯的亮灭,如果重新设置led灯,可以控制全部的led灯的亮灭,但是在退出测试程序的时候会出错,有点类似于程序无法关闭,退出之后按键仍然可以控制led灯。

IO_irq.h

#include /* needed for the _IOW etc stuff used later */

#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    /* printk() */

#include      /* kmalloc() */

#include        /* everything... */

#include     /* error codes */

#include     /* size_t */

#include

#include     /* O_ACCMODE */

#include

#include

#include

#include /* current and everything */

//#include   /* udelay */

 

#include      /* cli(), *_flags */

#include     /* copy_*_user */

#include

 

#include "IO_irq.h"     /* local definitions */

 

#include

 

#include //自动创建

 

#include //ssleep

 

 

#include

 

#include /*端口定义*/

#include

 

#include /*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");

阅读(794) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~