Chinaunix首页 | 论坛 | 博客
  • 博客访问: 196156
  • 博文数量: 67
  • 博客积分: 3415
  • 博客等级: 中校
  • 技术积分: 860
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-27 17:05
文章分类

全部博文(67)

文章存档

2010年(64)

2009年(3)

我的朋友

分类: LINUX

2010-01-19 18:10:49

mini2440button驱动程序分析
By Jeefjiang JUL,5th,2009
开发板:友善之臂mini2440
开发环境:Fedora8+arm-linux-gcc+NFS
内核版本:Linux2.6.29
参考程序:LDD3的SCULL模块程序,友善之臂本身提供的button驱动
知识准备以及参考资料:
驱动程序方面:内核中断处理 LDD3 CHAP10
              poll和异步通知:LDD3 CHAP06
测试程序方面:poll :UNIX环境高级编程 chap12
              异步通知:UNIX环境高级编程 chap10
网络资源:《使用异步 I/O 大大提高应用程序的性能》 IBM中文技术网站
硬件引脚配置:    
S3C2410_GPG0,
    S3C2410_GPG3,
    S3C2410_GPG5,
    S3C2410_GPG6,
    S3C2410_GPG7,
    S3C2410_GPG11,
设备结构体:
struct button_dev{
    unsigned long button_tab[6];
    unsigned long irq_tab[6];
    unsigned long button_cfg_tab[6];
    char *btn_name[6];
    struct cdev button_cdev;
    struct fasync_struct *async_queue; /* asynchronous readers */
//    struct semaphore sem;
    wait_queue_head_t outq;
};
采用LDD3中常用的嵌入cdev的方法。
中断处理:
1)    注册:我们按照LDD3的要求,在打开设备时进行注册,这样可以充分利用有限的中断线,注意这个程序是有缺陷的,如果在注册中出现问题,没有进行释放,表明我们的按键是随机产生的
static int minibtb_open(struct inode *inode, struct file *filp)
{
    struct button_dev  *dev; /* device information */
       int result,i;
    dev = container_of(inode->i_cdev, struct button_dev,button_cdev);
    filp->private_data = dev; /* for other methods */
    for(i=0;i<6;i++){
        result = request_irq(dev->irq_tab, minibtn_interrupt,
                IRQF_SAMPLE_RANDOM|IRQ_TYPE_EDGE_BOTH,dev->btn_name,
                 (void *)dev);
//dev成员是在ISR中需要用到的设备的私有结构

        if (result) {
            printk(KERN_INFO "button: can't get assigned irq key-%i\n", i);
               }
    }
    return 0;          /* success */
}
释放:
static int minibtn_release(struct inode *inode, struct file *filp)
{
    ……..
        for (i = 0; i < 6; i++) {
    free_irq(dev->irq_tab, (void *)dev);
    }
}
这样当设备文件打开时,才会在/proc/interrupt下观察到已经注册的中断,关闭设备文件时就没了。
POLL/SELECT
POLL/SELECT轮询是属于异步阻塞 I/O
带 有阻塞通知的非阻塞 I/O。在这种模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个 I/O 描述符何时有操作。使 select 调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用 以及是否发生错误的通知。
异步阻塞 I/O 模型的典型流程 (select)

注意这里的带有阻塞的通知时指当轮询时发现没有一个驱动程序可以进行非阻塞I/O,则POLL调用就进入休眠。
POLL驱动实现
static unsigned int minibtn_poll(struct file *filp, poll_table *wait)
{
    struct button_dev  *dev = filp->private_data;
    unsigned int mask = 0;
    poll_wait(filp, &dev->outq,  wait);
//在等待队列上调用poll_wait,则内核使该进程在传递到该系统调用的所有文件描叙符上等待对应得队列

       if (ev_press )//轮询条件满足时,设置掩码,表示可读
        mask |= POLLIN | POLLRDNORM;    /* readable */
              
            return mask;
}
POLL/SELECT的主要问题是它的效率不是非常高。尽管这是异步通知使用的一种方便模型,但是对于高性能的 I/O 操作来说不建议使用。因为老在轮询啊,这很浪费资源。

异步通知:
这种机制类似于中断,当有数据可读时,发出一个信号,通知进程可以去取按键值。应用程序需要定义信号处理程序,在产生指定的信号时就会调用这个处理程序。应用程序然后配置一个异步请求将在请求完成时产生一个信号。
这种情况下,IO可以是非阻塞的,通知也是非阻塞的。
异步通知的驱动实现:
static int minibtn_fasync(int fd, struct file *filp, int mode)
{
    struct button_dev *dev = filp->private_data;

    return fasync_helper(fd, filp, mode, &dev->async_queue);
}
当数据到达时必须发信号:
  if (dev->async_queue)
        kill_fasync(&dev->async_queue, SIGIO, POLL_IN); (在中断服务程序中)
POLL_IN表示有数据可读了
关闭文件时需要做的善后工作:
static int minibtn_release(struct inode *inode, struct file *filp)
{……….
   minibtn_fasync(-1, filp, 0);
………….
}
纠正一个笔误
复制代码
  1.     for(i=0;i<6;i++){
  2.         result = request_irq(dev->irq_tab, minibtn_interrupt,


应该是
复制代码
  1.     for(i=0;i<6;i++){
  2.         result = request_irq(dev->irq_tab[i], minibtn_interrupt,

才对^_^
阅读(519) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~