Button驱动学习

是一个char字符类型的驱动

按键与一个中断相连,所以在open的时候要申请一个中断,其中会包含

static int s3c24xx_buttons_open(struct inode *inode, struct file *file)

{

int i;

int err = 0;

for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

if (button_irqs[i].irq < 0) {

continue;

}

err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,

button_irqs[i].name, (void *)&button_irqs[i]);

if (err)

break;

}

if (err) {释放资源

}

return -EBUSY;

}

ev_press = 1;

return 0;

}

为每一个button都申请一个中断号,dev_id就是该button的数组button_irqs的一维的地址,中断发生后,dev_id就会传到中断服务函数中,中断服务函数根据dev_id判断是那个button按下了。

request_irq(IRQ_EINT8, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,

"KEY0", (void *)&button_irqs[i]);

buttons_interrupt是与该中断号对应的中断服务程序,dev_id定义为button_irqs一维的地址?

int request_irq(unsigned int irq,

irq_handler_t handler,

unsigned long flags, const char *devname, void *dev_id)

参数说明

在发生对应于第 1个参数 irq 的中断时,则调用第 2 个参数 handler 指定的中断服务函数(也就是把 handler() 中断服务函数注册到内核中 )。

第 3 个参数 flags 指定了快速中断或中断共享等中断处理属性。在 2.6 教新的内核里(我的是 2.6.27 ~ 2.6.31 ),在 linux/interrupt.h 中定义操作这个参数的宏如下:

第 4 个参数 name 通常是设备驱动程序的名称。改值用在 /proc/interrupt 系统 (虚拟) 文件上,或内核发生中断错误时使用。

第 5 个参数 dev_id 可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址。

返回值

函数运行正常时返回 0 ,否则返回对应错误的负值。

static struct button_irq_desc button_irqs [] = {

{IRQ_EINT8 , S3C2410_GPG0 , S3C2410_GPG0_EINT8 , 0, "KEY0"},

{IRQ_EINT11, S3C2410_GPG3 , S3C2410_GPG3_EINT11 , 1, "KEY1"},

{IRQ_EINT13, S3C2410_GPG5 , S3C2410_GPG5_EINT13 , 2, "KEY2"},

{IRQ_EINT14, S3C2410_GPG6 , S3C2410_GPG6_EINT14 , 3, "KEY3"},

{IRQ_EINT15, S3C2410_GPG7 , S3C2410_GPG7_EINT15 , 4, "KEY4"},

{IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 5, "KEY5"},

};

static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};

在初始化代码中就是注册一下驱动。

static int __init dev_init(void)

{

int ret;

ret = misc_register(&misc);

printk (DEVICE_NAME"\tinitialized\n");

return ret;

}

中断服务函数

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

int down;

// udelay(0);

down = !s3c2410_gpio_getpin(button_irqs->pin);

//按下down = 1 = !0 //松开down = 0 = !1

if (down != (key_values[button_irqs->number] & 1)) { // Changed

//1 != 0&1 //0 != 1&1 &优先级比!=低

key_values[button_irqs->number] = '0' + down;

// = ‘0’+1 = ‘1’ // = ‘0’+0 = ‘0’

ev_press = 1;

wake_up_interruptible(&button_waitq);

}

return IRQ_RETVAL(IRQ_HANDLED);

}

更新key_values将其传给read

执行机制:

通过一个等待队列实现wince中的事件同步机制

什么是等待队列?

在软件开发中任务经常由于某种条件没有得到满足而不得不进入睡眠状态,然后等待条件得到满足的时候再继续运行,进入运行状态。这种需求需要等待队列机制的支持。Linux中提供了等待队列的机制,该机制在内核中应用很广泛。

在Linux内核中使用等待队列的过程很简单,首先定义一个wait_queue_head,然后如果一个task想等待某种事件,那么调用wait_event(等待队列,事件)就可以了。

clip_image002

首先创建一个等待队列头button_waitq

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

App中调用read函数,驱动中调用s3c24xx_buttons_read函数,

static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

unsigned long err;

if (!ev_press) {

if (filp->f_flags & O_NONBLOCK)

return -EAGAIN;

else

wait_event_interruptible(button_waitq, ev_press); // ev_press是等待条件

}

ev_press = 0;

err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

return err ? -EFAULT : min(sizeof(key_values), count);

}

s3c24xx_buttons_read中添加一个事件到等待队列button_waitq中,其中ev_press是等待条件,那么App中的read函数调用驱动中的s3c24xx_buttons_read函数,此函数会阻塞在wait_event_interruptible处等待中断。

然后,如果产生了中断,中断服务函数中的:

key_values[button_irqs->number] = '0' + down;

ev_press = 1;

wake_up_interruptible(&button_waitq);

会唤醒等待队列button_waitq中的可以被中断的任务,同时设置ev_press为1

这样,s3c24xx_buttons_read中的阻塞函数将得以执行下去,对等待条件复位,复制键值。

原文地址:https://www.cnblogs.com/yanhc/p/2175221.html