Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537119
  • 博文数量: 70
  • 博客积分: 3162
  • 博客等级: 中校
  • 技术积分: 850
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-23 13:30
文章分类
文章存档

2013年(1)

2012年(4)

2011年(1)

2010年(7)

2009年(9)

2008年(20)

2007年(3)

2006年(25)

分类: LINUX

2008-12-08 11:52:56


linux2.6内核下的一个按键中断驱动程序示例【转载】

本程序是基于三星s3c2410ARM平台下的按键驱动例程,程序中两个按键分别占用了ARM芯片上的外部中断16和17,程序中设定外部中断为下降沿响应中断.本程序是在2.6.16内核版本下编译测试通过,交叉编译器采用3.4.1版本的arm-linux-gcc.

1.驱动程序文件名为button.c,其源码如下示:
/**********************start*******************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#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   16
#define BUTTONSTATUS_2   17

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

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=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***************************/  

2.   当然在编写2.6内核驱动程序之前应该已经自己建立好一个2.6的内核源码树(我这里是基于s3c2410移植的源码树,本处该源码是放在宿主机的 /home/src/linux-2.6.16目录下的),如果没有的话,那么需要自己去建立好这个源码树.自己编写的模块化驱动程序可以不放在内核源码之内,但是此外还需要一个自己编写一个Makefile文件(该文件和上面的button.c文件应放在同一个目录下),其内容如下示:

ifneq ($(KERNELRELEASE),)
     obj-m :=button.o
else
     KERNELDIR ?= /home/src/linux-2.6.16
     PWD := $(shell pwd)
default:
     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
endif

3.在宿主机的终端下,进入驱动程序目录内,敲入命令:
#make
就会在该目录下生成button.ko文件,这就是2.6内核下生成的驱动加载模块,注意是.ko文件,不同于2.4内核下的.o文件.
把该button.ko文件拷贝到目标板上,在minicom终端下进入该文件目录,敲入:
#insmod button.ko
如果终端显示有buttoninitialized则表示加载成功.
这时可以用命令lsmod查看动态加载模块:
#lsmod
当然,可以用如下命令查看devfs文件系统信息:
#cat /proc/devices
如果用卸载该模块,敲入命令:
#rmmod button

4.加载驱动程序后,可以自己再编写一个简单的测试程序,如下:
/**************************start***********************/
#include
#include
#include
#include
#include
#include
int main()

    int retval;
    int fileno;
    int ts, maxfd;
    int    ret= 0,i,j;
    int number;
    fileno = open("/dev/button",O_RDWR);
    if (fileno == -1) {
        printf("open device led errr!\n");
        return 0;
    }
    while(1) {
        read(fileno,&number,1);
        printf("key=0x%x\n",number);
    }
    close(fileno);
    return 0;
}
/**************************end***********************/

命名为test.c,并交叉编译该文件:
#arm-linux-gcc test.c -o test
将二进制文件同样拷贝到目标板上,运行:
#./test &
即可看到实验效果
原文地址
阅读(846) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~