按键驱动中:
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在什么地方睡下的了,呵呵。
阅读(2344) | 评论(0) | 转发(1) |