Chinaunix首页 | 论坛 | 博客
  • 博客访问: 624096
  • 博文数量: 144
  • 博客积分: 5037
  • 博客等级: 大校
  • 技术积分: 1581
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-30 21:49
文章存档

2010年(16)

2009年(128)

分类: LINUX

2009-06-21 14:43:29

   设备驱动的中断事件处理如下图所示,他与普通驱动的不同之处在于多了个数据缓冲区,驱动程序对上层提供的read/write方法并不直接完成硬件的数据操作,中断处理程序也是相对独立,他们通过缓冲区交换数据。而数据缓冲区的数据可通过FIFO进行读和写的操作,但每次只能进行读或者写的操作,两者不可同时进行,这就又涉及到了并发请求。并发请求类似信号量中的PV操作,对数据缓冲区的读写就相当于之前的生产者和消费者到仓库的存取。

中断事件涉及的函数
头文件
#include <asm/irq.h>
申请中断函数,成功返回0
int request_irq(unsigned int irq, void(*handler)(int,void*,struct pt_regs*), unsigned long irqflag,
const char *devname, void *dev_id);
//irq:中断号

// irqflag:

// SA_INTERRUPT,快速中断,执行handler函数时不能被打断

// SA_SHIRQ,共享中断, ,执行handler函数时能被打断

// SA_SAMPLE_RANDOM,中断可能被用来产生随机数

// dev_id: 在共享中断时用于区分不同的中断处理程序

// devname:中断设备名,在中断下运行cat /dev/interrupt 可查看到

释放中断函数
void free_irq(unsigned int irq, void *dev_id);
#include <asm/irq.h>
使能中断函数
void enable_irq(unsigned int irq);
禁止中断函数
void disable_irq(unsigned int irq);

S3C2410使能GPIO的外部中断功能
int set_external_irq(int irq, int edge, int pullup);
// edge:

// EXT_LOWLEVEL,

// EXT_HIGHLEVEL,

// EXT_FALLING_EDGE,

// EXT_RISING_EDGE,

// EXT_BOTH_EDGES

中断处理程序示例
void handler(int irq, void *dev_id, struct pt_regs *regs)
{
    // 中断处理

}
在初始化时申请并初始化中断
set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0);
request_irq(IRQ_EINT0,handler,SA_INTERRUPT,"KEY",NULL);
注意:要把使能外部中断函数set_external_irq放在中断请求函数request_irq之前,否则,会有小麻烦。
并发请求涉及的函数
使用信号量控制并发请求
#include <asm/semaphore.h>
定义信号量变量
struct semaphore bufflock;
初始化信号量
void sema_init(struct semaphore *sem, int);
获取(等待)信号量(不可打断)(相当于V操作)
void down(struct semaphore *sem);
获取(等待)信号量(可被打断)
int down_interruptible(struct semaphore *sem);
释放信号量(相当于P操作)
void up(struct semaphore *sem);
FIFO队列
    由于队列具有先进先出的功能,所以它能符合充当数据缓冲区的功能。我们可以自己写一个FIFO函数实现读和写的功能,也可调用/kernel/kernel/fifo.c 的函数。如果要调用/kernel/kernel/fifo.c,编译时要注意,可用命令进行:/usr/local/arm/2.95.3/bin/arm-linux-gcc -c -o kfifo.o kfifo.c -I/home/su/kernel/include -D__KERNEL__ -DMODULE -DEXPORT_SYMTAB –DMODVERSIONS。当我们完成编译后,要先加载kfifo.o,再加载我们自己写的驱动,否则会出错。以下以调用/kernel/kernel/fifo.c为例。
#include <linux/kfifo.h>
首先需要定义一个自旋锁,该锁由kfifo维护,我们只需要定义它即可

static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;
然后定义一个kfifo指针, 注意, 这里定义指针即可,不用分配空间

static struct kfifo *buffer;
使用kfifo_alloc可以创建一个BUFFER_SIZE大小的fifo, 所有空间由kfifo自动分配

#define BUFFER_SIZE 256
buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock);
使用kfifo_put可以将数据放入kfifo内
kfifo_put(buffer, &key, sizeof(key));

使用kfifo_len可以检查fifo内的可用数据
if(kfifo_len(buffer) >= sizeof(key))

使用kfifo_get可以从fifo内取出数据
kfifo_get(buffer, &key, sizeof(key));

最后退出时,释放buffer的内存空间
kfifo_free(buffer);
附:基于凌阳SPCE3200实验箱的4x4键盘的驱动(中断法实现)源代码
这里有两个需要注意的地方:
1、头文件的摆放顺序:#include <linux/kfifo.h>要放在 #include <asm/irq.h>之后,否则编译出错,因为asm/irq.h 里面定义了一些linux/kfifo.h 要用到的宏,若先写#include <linux/kfifo.h> ,加载驱动时,编译器会把里头的宏当成函数处理,但又找不到相应的函数,所以出错。这样的头文件出错,出错非常隐蔽,解决的办法唯有一步一步跟错查找。

2、该驱动的等待信号量函数应使用可打断类型的int down_interruptible(struct semaphore *sem),否则运行应用程序时,按下ctrl+c程序没法正常退出,还需按下实验箱上的任意一个按键。因为该中断是可被打断类型的,所以能接受响应外部的另外的中断,即能退出程序。
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/init.h> /* __init __exit */
#include <linux/types.h> /* size_t */
#include <linux/fs.h> /* file_operation */
//#include /* Error number */

#include <linux/delay.h> /* udelay */
#include <linux/timer.h>
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
#include <asm/hardware.h>
#include <asm/semaphore.h>
#include <asm/irq.h>
#include <asm/arch/S3C2410.h>
#include <linux/kfifo.h>

#define DRIVER_NAME "key"
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_NOTICE fmt, ##arg)
#else
#define PRINTK(fmt, arg...)
#endif
#define BUFFER_SIZE 256

static int keyDriver_Major = 0; /* Driver Major Number */
static int keyNum;
struct semaphore bufflock;
static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;
static struct kfifo *buffer;

void handler(int irq, void *dev_id, struct pt_regs *regs)
{
       int c_key,r_key,key;
       
       disable_irq(IRQ_EINT0);
       disable_irq(IRQ_EINT1);
       disable_irq(IRQ_EINT2);
       disable_irq(IRQ_EINT3);
  
       GPFCON = 0x0055;
       GPFDAT = 0xf0;
       mdelay(1);
       
       c_key = GPFDAT & 0xf0; //获得行标低四位

       GPFCON = 0x5500;
       GPFDAT = 0x0f;
       udelay(30);
       r_key = GPFDAT & 0x0f; //获得列标高四位

       key = ~( c_key | r_key) & 0xff;
// PRINTK("key = 0x%x\n", key);

       switch (key)
        {
            case 0x88:
                   PRINTK("key16 \n");
                   keyNum = 16;
                   break;
            case 0x84:
               PRINTK("key15 \n");
               keyNum = 15;
               break;
            case 0x82:
                   PRINTK("key14 \n");
                   keyNum = 14;
                   break;
            case 0x81:
                   PRINTK("key13 \n");
                   keyNum = 13;
                   break;
            case 0x48:
                   PRINTK("key12 \n");
                   keyNum = 12;
                   break;
            case 0x44:
                   PRINTK("key11 \n");
                   keyNum = 11;
                   break;
            case 0x42:
                  PRINTK("key10 \n");
                  keyNum = 10;
                  break;
            case 0x41:
                   PRINTK("key9 \n");
                   keyNum = 9;
                   break;
            case 0x28:
                   PRINTK("key8 \n");
                   keyNum = 8;
                   break;
            case 0x24:
              PRINTK("key7 \n");
                   keyNum = 7;
                   break;
            case 0x22:
                   PRINTK("key6 \n");
                   keyNum = 6;
                   break;
            case 0x21:
                   PRINTK("key5 \n");
                   keyNum = 5;
                   break;
             case 0x18:
                 PRINTK("key4 \n");
                   keyNum = 4;
                   break;
            case 0x14:
                   PRINTK("key3 \n");
                   keyNum = 3;
                   break;
            case 0x12:
                   PRINTK("key2 \n");
                   keyNum = 2;
                   break;
            case 0x11:
                   PRINTK("key1 \n");
                   keyNum = 1;
                   break;
              default:
                   break;
        }
        if(keyNum > 0)
        {
            kfifo_put(buffer, &keyNum, sizeof(keyNum));
            up(&bufflock);
        }
//PRINTK("keyNum = %d\n", keyNum);

//PRINTK("GPFCON = 0x%x GPFUP = 0x%x GPGDAT = 0x%x\n", GPFCON, GPFUP, GPFDAT);

        // GPFCON = (GPFCON | 0xffff) & 0x55AA;

        GPFCON = 0x55AA;
        GPFUP=0x00;
      GPFDAT = 0x00;
      
      enable_irq(IRQ_EINT0);
     enable_irq(IRQ_EINT1);
      enable_irq(IRQ_EINT2);
      enable_irq(IRQ_EINT3);
}
/* Driver Operation Functions */
static int keyDriver_open(struct inode *inode, struct file *filp)
{
       MOD_INC_USE_COUNT;

       PRINTK("keyDriver open called!\n");
       enable_irq(IRQ_EINT0);
       enable_irq(IRQ_EINT1);
       enable_irq(IRQ_EINT2);
       enable_irq(IRQ_EINT3);
      sema_init(&bufflock, 0);
       buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock);
       return 0;
}
static int keyDriver_release(struct inode *inode, struct file *filp)
{
       MOD_DEC_USE_COUNT;
       PRINTK("keyDriver release called!\n");
       disable_irq(IRQ_EINT0);
       disable_irq(IRQ_EINT1);
       disable_irq(IRQ_EINT2);
       disable_irq(IRQ_EINT3);
       
       kfifo_free(buffer);
       return 0;
}
static ssize_t keyDriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
       size_t read_size = count;
       PRINTK("myDriver read called!\n");
       PRINTK("\tcount=%d, pos=%d\n", count, (int)*f_pos);
      
       down_interruptible(&bufflock); //若这里使用down的话,运行应用程序时会出点小问题

       kfifo_get(buffer, &keyNum, sizeof(keyNum));
       copy_to_user(buf, &keyNum, read_size);
       *f_pos += read_size;
       return read_size;
}
static struct file_operations keyDriver_fops = {
      owner: THIS_MODULE,
// write: keyDriver_write,

      read: keyDriver_read,
// ioctl: keyDriver_ioctl,

      open: keyDriver_open,
      release: keyDriver_release,
};
#ifdef CONFIG_DEVFS_FS
devfs_handle_t devfs_keyDriver_dir;
devfs_handle_t devfs_keyDriver_raw;
#endif
static int __init myModule_init(void)
{
       GPFCON = 0x5500; //GPF4,5,6,7 设置为输出

       GPFUP = 0x00; //GPF4、5、6、7上拉使能

       GPFDAT = 0x00; //GPF4,5,6,7 输出低电平

       
       set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0);
       request_irq(IRQ_EINT0, handler, SA_INTERRUPT, "KEY1", NULL);
       
       set_external_irq(IRQ_EINT1, EXT_FALLING_EDGE, 0);
       request_irq(IRQ_EINT1, handler, SA_INTERRUPT, "KEY2", NULL);
       
       set_external_irq(IRQ_EINT2, EXT_FALLING_EDGE, 0);
       request_irq(IRQ_EINT2, handler, SA_INTERRUPT, "KEY3", NULL);
       
       set_external_irq(IRQ_EINT3, EXT_FALLING_EDGE, 0);
       request_irq(IRQ_EINT3, handler, SA_INTERRUPT, "KEY4", NULL);
       
       /* Module init code */
       PRINTK("myModule_init\n");
       /* Driver register */
       keyDriver_Major = register_chrdev(0, DRIVER_NAME, &keyDriver_fops);
       if(keyDriver_Major < 0)
       {
              PRINTK("register char device fail!\n");
              return keyDriver_Major;
       }
       PRINTK("register keyDriver OK! Major = %d\n", keyDriver_Major);
#ifdef CONFIG_DEVFS_FS
       devfs_keyDriver_dir = devfs_mk_dir(NULL, "keyDriver", NULL);
       devfs_keyDriver_raw = devfs_register(devfs_keyDriver_dir, "raw0", DEVFS_FL_DEFAULT, keyDriver_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &keyDriver_fops, NULL);
       PRINTK("add dev file to devfs OK!\n");
#endif
       return 0;
}
static void __exit myModule_exit(void)
{
       /* Module exit code */
       PRINTK("myModule_exit\n");
       free_irq(IRQ_EINT0, NULL);
       free_irq(IRQ_EINT1, NULL);
       free_irq(IRQ_EINT2, NULL);
       free_irq(IRQ_EINT3, NULL);
       
       /* Driver unregister */
       if(keyDriver_Major > 0)
       {
#ifdef CONFIG_DEVFS_FS
              devfs_unregister(devfs_keyDriver_raw);
              devfs_unregister(devfs_keyDriver_dir);
#endif
              unregister_chrdev(keyDriver_Major, DRIVER_NAME);
       }
       return;
}
MODULE_AUTHOR("SXZ");
MODULE_LICENSE("Dual BSD/GPL");
module_init(myModule_init);
module_exit(myModule_exit);

应用程序源代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
       int fd;
       unsigned char key;

       if((fd = open("/dev/keyDriver/raw0", O_RDONLY)) < 0)
       {
              printf("can't open keyDriver\n");
              exit(1);
       }
       while(1)
       {
              read(fd, &key, 1);
              printf("program_key = %d\n", key);
       }
       close(fd);
       return 0;
}

注:小志整理

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