Chinaunix首页 | 论坛 | 博客
  • 博客访问: 407743
  • 博文数量: 62
  • 博客积分: 1483
  • 博客等级: 上尉
  • 技术积分: 779
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-24 12:25
文章分类

全部博文(62)

文章存档

2012年(2)

2011年(6)

2010年(6)

2009年(48)

我的朋友

分类: LINUX

2009-10-22 17:26:00

按键驱动中:
1。引脚中断的触发方式的配置
2。注销irq中断的方式
3。等待队列的使用
4。poll函数的使用

1。引脚中断的触发方式的配置
set_irq_type(button_irqs[i].irq, IRQT_BOTHEDGE);
上面的函数调用具体中断号(button_irqs[i].irq)的desc->chip->type()
函数来完成对外中断的触发方式的配置。

外中断的触发方式有 高电平 低电平 上升沿 下降沿 上升/下降沿 五种触发方式,
这5种触发方式由 2440的gpio配置为中断的情况下,在由gpio寄存器组中的
EXTINTn (External Interrupt Control Register n)寄存器配置
为相应的5种触发方式之1来使用。

下面是2440中的 EINT7 触发方式的描述,其他的一样。
Setting the signaling method of the EINT7.
000 = Low level      001 = High level    01x = Falling edge triggered
10x = Rising edge triggered                11x = Both edge triggered

但是 IRQT_BOTHEDGE 这个预定义并不与上面的相对应

#define __IRQT_FALEDGE    (1 << 0)
#define __IRQT_RISEDGE    (1 << 1)
#define __IRQT_LOWLVL    (1 << 2)
#define __IRQT_HIGHLVL    (1 << 3)

#define IRQT_NOEDGE    (0)
#define IRQT_RISING    (__IRQT_RISEDGE)
#define IRQT_FALLING    (__IRQT_FALEDGE)
#define IRQT_BOTHEDGE    (__IRQT_RISEDGE|__IRQT_FALEDGE)
#define IRQT_LOW    (__IRQT_LOWLVL)
#define IRQT_HIGH    (__IRQT_HIGHLVL)
#define IRQT_PROBE    (1 << 4)

这是因为他们是对中断触发方式的进一步抽象,具体的配置由s3c_irqext_type()函数来设置

    switch (type)
    {
        case IRQT_NOEDGE:
            printk(KERN_WARNING "No edge setting!\n");
            break;

        case IRQT_RISING:
            newvalue = S3C2410_EXTINT_RISEEDGE;
            break;

        case IRQT_FALLING:
            newvalue = S3C2410_EXTINT_FALLEDGE;
            break;

        case IRQT_BOTHEDGE:
            newvalue = S3C2410_EXTINT_BOTHEDGE;
            break;

        case IRQT_LOW:
            newvalue = S3C2410_EXTINT_LOWLEV;
            break;

        case IRQT_HIGH:
            newvalue = S3C2410_EXTINT_HILEV;
            break;

        default:
            printk(KERN_ERR "No such irq type %d", type);
            return -1;
    }

    value = __raw_readl(extint_reg);
    value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
    __raw_writel(value, extint_reg);

/* values for S3C2410_EXTINT0/1/2 */
#define S3C2410_EXTINT_LOWLEV     (0x00)
#define S3C2410_EXTINT_HILEV     (0x01)
#define S3C2410_EXTINT_FALLEDGE     (0x02)
#define S3C2410_EXTINT_RISEEDGE     (0x04)
#define S3C2410_EXTINT_BOTHEDGE     (0x06)

上面这些配置才对应2440 的中断触发方式。

s3c_irqext_type()还附加了对gpio引脚配置为中断功能的代码, 但是 s3c2410_gpio_cfgpin() 之前已经配置了相应功能。
所以他的操作是多余的,但是好处是保证了引脚的正确配置,即使你忘了对gpio引脚的配置。

2。注销irq中断的方式

for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
        disable_irq(button_irqs[i].irq);
        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }
disable_irq()函数宣布 这是对此中断的最后使用,以后不再处理这个中断了,然后注销irq中断。
从逻辑上保证了在释放某个中断前夕能够得到正确的按键中断处理。

3。等待队列的使用
ldd中等待队列的使用是高级字符设备,在按键驱动中他的使用并不复杂
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
在读不到按键数据时
wait_event_interruptible(button_waitq, ev_press);
ev_press = 0;
当发生按键事件时,按键中断负责唤醒挂在等待队列上的进程。
ev_press = 1;
wake_up_interruptible(&button_waitq);

4。poll函数的使用
static unsigned int qq2440_buttons_poll(
        struct file *file,
        struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait);
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}

一直不太明白select()是怎么阻塞在他想打开的文件描述符集合中的,看按键的驱动poll里
poll_wait(file, &button_waitq, wait);这个句子,起初以为是他导致的阻塞,看ldd
,上面说这个函数并不是引起阻塞的原因,于是看了看系统调用sys_select()。
才发现,真正引起阻塞的调用是do_select()。

poll_wait()函数的作用是把button_waitq这个按键等待队列放到struct poll_table_struct *wait上,
具体的插入函数是由__pollwait()完成的。

struct poll_table_struct *wait上的等待队列是给do_select()用的,do_select()会检测文件描述符
集中的所有描述符,并调用具体的poll函数(比如qq2440_buttons_poll()),如果poll一直没有结果(retval),
他会在无限循环中等待。等待的方式肯定不会是无限的扫描,那样太浪费cpu时间,应该是调用cond_resched();
让出cpu。

do_select() 片段如下:

for (;;) {//无限循环,他的出口在下面

for (i = 0; i < n; ++rinp, ++routp, ++rexp) { //扫描所有的文件描述符集
...
if (f_op && f_op->poll)
    mask = (*f_op->poll)(file, retval ? NULL : wait);
...

        if (retval || !__timeout || signal_pending(current))
            break;
        if(table.error) {
            retval = table.error;
            break;
        }

上面的两个if是无限循环的出口,可以看出,超时 有返回值 有信号 都会使do_select()返回的。


这里的wait就是传递给qq2440_buttons_poll的wait了,

那么do_select()为什么要一个等待队列呢?因为这个系统调用会被抢占
cond_resched();

这个函数可以让出cpu的,假设:

现在do_select让出了cpu(因为我们没有按键中断),当我们的按键被按下或者松开时,就会产成
按键中断,而按键中断负责唤醒等待在这个等待队列上的进程,这时do_select()从cond_resched()继续运行,
发现qq2440_buttons_poll()返回了我们想要的   ”可以读数据(mask |= POLLIN | POLLRDNORM;)“  标志。
do_select()完成任务,从阻塞中返回,程序继续下次select()调用。

[root@FriendlyARM /]# buttons &
[root@FriendlyARM /]# ps
  PID  Uid     VmSize Stat Command
    1 root        292 S   init     
...
                                                  
  302 root        348 S   buttons
  322 root        664 R   ps 

buttons正在sleep呢,现在我知道buttons在什么地方睡下的了,呵呵。 
 
阅读(2339) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~