Chinaunix首页 | 论坛 | 博客
  • 博客访问: 139382
  • 博文数量: 49
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 515
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-08 10:33
文章分类

全部博文(49)

文章存档

2010年(2)

2009年(30)

2008年(17)

我的朋友

分类: LINUX

2009-04-09 14:39:41

以下是一个使用中断的按键驱动程序。内核采用2.6.15.1,编译器采用3.4.3release版本的)。在arm2410开发板上测试通过。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include 3c2410/regs-gpio.h>

 

#include

#include 3c2410/irqs.h>

#include

#include

#include

 

#define BUTTON_IRQ   IRQ_EINT1     //申请的中断号

#define DEVICE_NAME       "button"      //设备名字

 

#define  BUTTONMINOR   0

 

 

DECLARE_WAIT_QUEUE_HEAD(wq);             //申请等待队列并初始化

static unsigned char key_down_cnt=0;        //中断计数

 

static int ev_press=0;                             //唤醒等待的条件

static  int button_Major;                            //设备号

 

static irqreturn_t  button_irq_ser(int irq,void *dev_id,struct pt_regs *regs)  //中断服务程序

{

    key_down_cnt++;                                                          //来一次,中断计数加一

    ev_press = 1;                                                                 //表示中断发生了

    wake_up_interruptible(&wq);                                                 //唤醒休眠的进程

    return 0;    

}

 

static int  s3c2410_button_open(struct inode *inode,struct file *filp)    //open 函数

{

    int ret;

    ret= request_irq(BUTTON_IRQ,button_irq_ser,SA_INTERRUPT,DEVICE_NAME,NULL);    //注册中断

    if(ret<0)

       {

           printk("could not register interrupt !");

           return ret;

       }

    printk("interrupt register successed");

    return 0;

  

}

 

static int s3c2410_button_release(struct inode *inode,struct file *filp)  //关闭设备

{

    free_irq(BUTTON_IRQ,NULL);           //释放中断

    return 0;

}

//读设备

static ssize_t s3c2410_button_read(struct file *filp,char __user *buff,size_t count,loff_t *offp) 

{

   //printk("sleep\n");

   //如果ev_press = 0,则继续休眠

   wait_event_interruptible(wq,ev_press);

   //若执行到这儿,ev_press = 1

   printk("wake up \n");

   //清除ev_press

   ev_press = 0;

   //拷贝key_down_cnt 到用户空间

   copy_to_user(buff,(const void *)&key_down_cnt,sizeof(char));

   printk("process is waked up after");

  // key_down_cnt = 0;

   return sizeof(unsigned char);

  

}

 

static struct file_operations s3c2410_button_flops = {

        .owner    =   THIS_MODULE,

       .open     =   s3c2410_button_open,

       .read         =   s3c2410_button_read,

       .release  =   s3c2410_button_release,

};

//模块初始化

static int __init  s3c2410_button_init(void)

{

    int ret;

    set_irq_type(BUTTON_IRQ,IRQT_FALLING);   //设置触发中断的类型,下降沿触发

ev_press = 0;                                                   //初始化ev_press

//注册设备,返回主设备号

    ret=register_chrdev(0,DEVICE_NAME,&s3c2410_button_flops);  

    if(ret<0)

       {

            printk(DEVICE_NAME"can not register the number!");

            return ret;

       }

button_Major = ret;

//建立设备节点

#ifdef CONFIG_DEVFS_FS

    devfs_mk_cdev(MKDEV(button_Major,BUTTONMINOR),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);

#endif

    printk("button_init initialized successed!\n");

    return 0; 

}

//卸载模块

static void __exit s3c2410_button_exit(void)

{

#ifdef   CONFIG_DEVFS_FS

      devfs_remove(DEVICE_NAME);

#endif

      unregister_chrdev(button_Major,DEVICE_NAME);

}

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("YUAF");

MODULE_DESCRIPTION("the s3c2410 key interrupt driver");

 

module_init(s3c2410_button_init);

module_exit(s3c2410_button_exit);

 

在使用等待队列的时候,必须得初始化。

Init_waitqueue_head(&(buttondev.wq);

也可以用 DECLARE_WAIT_QUEUE_HEAD(buttondev.wq),初始化和申明都在这条语句里面。若不初始化,则会出现:

 

Unable to handle kernel NULL pointer dereference at virtual address 00000004

pgd = c3e64000

[00000004] *pgd=3381c031, *pte=00000000, *ppte=00000000

Internal error: Oops: 817 [#2]

Modules linked in: button

CPU: 0

PC is at prepare_to_wait+0x54/0x9c

LR is at prepare_to_wait+0x38/0x9c

pc : [0053c44>]    lr : [0053c28>]    Not tainted

sp : c3ecdee0  ip : c3ecdee0  fp : c3ecdefc

r10: c0378d00  r9 : c3ecc000  r8 : bf000e24

r7 : 00000001  r6 : 40000013  r5 : bf000e48  r4 : c3ecdf14

r3 : 00000000  r2 : c3ecdf20  r1 : c3ecc000  r0 : 00000001

Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  Segment user

Control: C000717F  Table: 33E64000  DAC: 00000015

Process test (pid: 828, stack limit = 0xc3ecc194)

Stack: (0xc3ecdee0 to 0xc3ece000)

dee0: c3ecdf10 c3ecdf14 c3ecc000 becf4e8c c3ecdf4c c3ecdf00 bf0002e0 c0053c00

df00: 00000000 c03f7080 c0053db8 c3ecdf20 c3ecdf20 00000000 c03f7080 c0053db8

df20: 00000000 c3ecdf20 00000001 c0378d00 becf4e8c 00000000 c3ecdf78 c3ecc000

df40: c3ecdf74 c3ecdf50 c007dee4 bf0001e0 c0378d20 c0378d00 c3ecdf78 00000000

df60: 00000000 40137e60 c3ecdfa4 c3ecdf78 c007e268 c007de34 00000000 00000000

df80: 00000000 becf4ed4 40020c34 000082c4 00000003 c0024104 00000000 c3ecdfa8

dfa0: c0023f80 c007e22c becf4ed4 40020c34 00000003 becf4e8c 00000001 becf4e8c

dfc0: becf4ed4 40020c34 000082c4 00000001 00008438 401377ec 40137e60 becf4eb8

dfe0: 400e1ef0 becf4e8c 000084a0 400e1ef4 60000010 00000003 00000000 00000000

Backtrace:

[] (prepare_to_wait+0x0/0x9c) from [] (button_read+0x110/0x1b4 [button])

 r7 = BECF4E8C  r6 = C3ECC000  r5 = C3ECDF14  r4 = C3ECDF10

[] (button_read+0x0/0x1b4 [button]) from [] (vfs_read+0xc0/0x17c)

[] (vfs_read+0x0/0x17c) from [] (sys_read+0x4c/0x74)

[21c>] (sys_read+0x0/0x74) from [0023f80>] (ret_fast_syscall+0x0/0x2c)

 r8 = C0024104  r7 = 00000003  r6 = 000082C4  r5 = 40020C34

 r4 = BECF4ED4

Code: 05953000 0584300c e3cd1d7f e3c1103f (05832004)

 <6>note: test[828] exited with preempt_count 1

Segmentation fault

出现这种错误,可知内核是因为非法地址访问出错,使用了空指针。

测试程序如下:

 

#include

#include

#include

#include

#include

#include

 

int main()

 

{

   int retval;

   int fileno;

   int ts, maxfd;

   int  ret= 0,i,j;

   int number=0;

   fileno = open("/dev/button",O_RDWR);

       if (fileno == -1) {

               printf("open device led errr!\n");

               return 0;

              }

  printf("open success\n");

   while(1) {

         read(fileno,&number,1);    

          printf("key=%d\n",number);

    }

   close(fileno);

   return 0;

}

仍需解决的问题:按键消抖的问题,这个程序,按一下,有时会出现多次中断。

 

这是网上的一个驱动程序。也能测试通过。程序中注释是自己添加的。

 

/**********************start*******************************/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include 3c2410/regs-gpio.h>

 

#include

#include 3c2410/irqs.h>

#include

#include

#include

//申请的中断号

#define     BUTTON_IRQ1 IRQ_EINT16     

#define     BUTTON_IRQ2 IRQ_EINT17

//设备名字

#define DEVICE_NAME "button"

static int buttonMajor=0;

#define     BUTTONMINOR 0

//缓冲区长度

#define     MAX_BUTTON_BUF  16

   

#define BUTTONSTATUS_1      1

#define BUTTONSTATUS_2      2

 

 

static unsigned char buttonRead(void);

static int flag=0;

 

typedef struct {

    unsigned int buttonStatus;      //按键状态

    unsigned char buf[MAX_BUTTON_BUF]; //按键缓冲区

    unsigned int head,tail;         //按键缓冲区头和尾

    wait_queue_head_t wq;           //等待队列

} BUTTON_DEV;

 

static BUTTON_DEV buttondev;

 

#define BUF_HEAD    (buttondev.buf[buttondev.head])     //缓冲区头

#define BUF_TAIL    (buttondev.buf[buttondev.tail])     //缓冲区尾

#define INCBUF(x,mod)   ((++(x)) & ((mod)-1))       //移动缓冲区指针,建立了环形队列

 

static void (*buttonEvent)(void);    //定义了一个返回值为void,不带参数的函数指针

 

static void buttonEvent_dummy(void) {}

 

static void buttonEvent_1(void)

{

    if(buttondev.buttonStatus==BUTTONSTATUS_2) {

    BUF_HEAD=BUTTONSTATUS_2;    //保存的是哪一个发生了中断

     }

    else {

    BUF_HEAD=BUTTONSTATUS_1;

     }

    /*移动缓冲区指针,也就是为判断中断buttondev.head!=buttondev.tail是否发生提供了依据*/

buttondev.head=INCBUF(buttondev.head,MAX_BUTTON_BUF);

//唤醒等待的标志位

flag=1;

//唤醒休眠的进程

    wake_up_interruptible(&(buttondev.wq));

    printk("buttonEvent_1\n");

}

//中断服务程序

static irqreturn_t isr_button(int irq,void *dev_id,struct pt_regs *regs)

{

   

    printk("Occured key board Inetrrupt,irq=%d\n",irq-44);

    switch (irq) {

    case BUTTON_IRQ1:buttondev.buttonStatus=BUTTONSTATUS_1;

                break;

    case BUTTON_IRQ2:buttondev.buttonStatus=BUTTONSTATUS_2;

                break;

    default:break;

     }

   

    buttonEvent();

    return 0;

}

 

static int button_open(struct inode *inode,struct file *filp)

{

    int ret;

    buttondev.head=buttondev.tail=0;

buttonEvent=buttonEvent_1; 

//注册中断

    ret=request_irq(BUTTON_IRQ1,isr_button,SA_INTERRUPT,DEVICE_NAME,NULL);

    if(ret) {

    printk("BUTTON_IRQ1: could not register interrupt\n");

    return ret;

     }

    ret=request_irq(BUTTON_IRQ2,isr_button,SA_INTERRUPT,DEVICE_NAME,NULL);

    if(ret) {

    printk("BUTTON_IRQ2: could not register interrupt\n");

    return ret;

     }

    return 0;

}

//关闭设备

static int button_release(struct inode *inode,struct file *filp)

{  

    buttonEvent=buttonEvent_dummy;

    free_irq(BUTTON_IRQ1,NULL);

    free_irq(BUTTON_IRQ2,NULL);

    return 0;

}

//设备读程序

static ssize_t button_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)

{

    static unsigned char button_ret;

 retry:

    printk("retry start\n");

    if(buttondev.head!=buttondev.tail) {  //判断是否发生了中断

button_ret=buttonRead();         //无效中断发生条件

//拷贝数据到用户空间

    copy_to_user(buffer,(char *)&button_ret,sizeof(unsigned char));

printk("the button_ret is 0x%x\n",button_ret);

//返回

    return sizeof(unsigned char);

     }

    else {//没有发生中断

    if(filp->f_flags & O_NONBLOCK)

        return -EAGAIN;

    printk("sleep\n");

//interruptible_sleep_on(&(buttondev.wq));//为安全起见,最好不要调用该睡眠函数

//等待发生中断

    wait_event_interruptible(buttondev.wq,flag);

    flag=0;

    printk("sleep_after\n");

    if(signal_pending(current))

         {

        printk("rturn -ERESTARTSYS\n");

        return -ERESTARTSYS;

          }

    goto retry;

     }

 

    return sizeof(unsigned char);

}

 

static struct file_operations button_fops= {

    .owner  =   THIS_MODULE,

    .open   =   button_open,

    .read   =   button_read,

    .release    =   button_release,

};

 

 

//模块初始化

static int __init s3c2410_button_init(void)

{

    int ret;

    set_irq_type(BUTTON_IRQ1,IRQT_FALLING);

    set_irq_type(BUTTON_IRQ2,IRQT_FALLING);

 

    buttonEvent=buttonEvent_dummy;

   

    ret=register_chrdev(0,DEVICE_NAME,&button_fops);

    if(ret<0) {

    printk("button: can't get major number\n");

    return ret;

     }

    buttonMajor=ret;

#ifdef  CONFIG_DEVFS_FS

 devfs_mk_cdev(MKDEV(buttonMajor,BUTTONMINOR),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);

#endif

    //buttondev.head=buttondev.tail=0;

    buttondev.buttonStatus=BUTTONSTATUS_1;

    init_waitqueue_head(&(buttondev.wq));

    printk(DEVICE_NAME"initialized\n");

    return 0;

}

//移动指针

static unsigned char buttonRead(void)

{

    unsigned char button_ret;

    button_ret=BUF_TAIL;

    buttondev.tail=INCBUF(buttondev.tail,MAX_BUTTON_BUF);

    return button_ret;

}

 //卸载模块时调用

static void __exit s3c2410_button_eixt(void)

{

 

 #ifdef CONFIG_DEVFS_FS

    devfs_remove(DEVICE_NAME);

   

 #endif

    unregister_chrdev(buttonMajor,DEVICE_NAME);

}

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Kision");

MODULE_DEscrīptION ("the first char device driver");

 

module_init(s3c2410_button_init);

module_exit(s3c2410_button_eixt);

/*************************end***************************/

 

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