内核中断及按键驱动程序

寒假Linux学习笔记

2015年1月25日 晚 20:00

一、内核中断处理

进程上下文:应用程序主动调用内核驱动的程序的跳转

中断上下文:中断由硬件产生的,与应用程序无关

 

 

1、注册中断

Int request_irq(unsigned int irq,             //中断号

void (*handler)(int ,void *, struct pt_regs*),    //中断处理函数

unsigned long flags,            //与中断处理管理相关的各种选项

const char * devname,        //设备名

void * dev_id                //共享中断时使用的id号,唯一

);

Flags参数:

    IRQF_DISABLED(SA_INTERRUPT);    //若设置此位,则是一个快速中断,即中断为原子操作,不会被打断;未设置该位,则是一个慢速中断

    IRQF_SHARED(SA_SHIRQ);        //中断可以在设备间共享,申请共享中断必须设置此位为IRQF_SHARED

 

注意:共享中断 不能使用 disable_irq(unsigned int irq),因为一旦使用该函数,则共享该中断线的全部都不能用

 

2、中断处理函数流程

Void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

    /*判断是否是本设备的中断,因为共享中断中使用的是同一个中断处理函数,此时内核就无法判断是哪一个设备发生的中断,可能产生误调用*/

    Value = inb(short_base);

If(!value & 0x80) return;

 

/*清除中断标志位(若设备支持自动清除,则不需要此位)*/

Outb(value & 0x7f, short_base);

 

/* 中断处理函数,数据处理 */

……

/* 唤醒等待的进程 */

Wake_up_interrruptible(&short_quue);

}

 

中断处理程序:(运行环境为中断上下文)

    1.不能向用户空间发送或接受数据,因为进程变了,用户就变了,中断不对应任何进程

    2.不能使用可能引起阻塞的函数

    3.不能使用可能引起调度的函数,中断中无进程,无法调度

 

 

3、释放中断

Void free_irq(unsigned int irq,void * dev_id);

当被释放的是共享中断时,必须设定dev_id位,否则共享中断全线不能用

 

 

二、按键驱动处理程序分析

分析实现流程:

        //混杂设备结构体,主设备为10,次设备号不同的一类设备

static struct miscdevice misc = {

    .minor = MISC_DYNAMIC_MINOR,        //次设备号

    .name = DEVICE_NAME,        //设备名

    .fops = &dev_fops            //字符设备的file_operation

};

1.初始化程序

Static int __init dev_init(void){

    Int ret;

    ret = misc_register(&misc);        //注册混杂设备

    ……

    return ret;

}

 

2.file_operations 按键实现的操作

static struct file_operations dev_fops = {

    .owner = buttons_open,    //打开操作

    .release = buttons_close,

    .poll = buttons_poll        //select,多路监控

};

//open 函数实现

Static buttons_open(static inode *inode,struct(file *file)){

        //为六个中断线分别注册驱动程序 总的大小/单个的大小=个数

    For(I = 0, i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++){    

        If(button_irqs[i].irq<0)        

            Continue;

//制定中断处理程序, IRQ_TYPE_EDGE_BOTH:双边沿产生中断

    request_irq(button_irqs[i].irq,buttons_interrupt,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name,(void *)&button_irqs[i]);

}

}

//poll 函数实现

static unsigned int buttons_poll(struct file *file,struct poll_table_struct *wait){

    poll_wait(file,&button_waitq,wait);        //指明要使用的等待队列

    if(ev_press)            //全局静态变量,初始值未按下为0,为1时按键按下

        mask |= POLLIN | POLLRDNORM;     //按键可读不可写

    return mask;        //返回掩码

}

//当按键按下,程序开始读,读函数实现

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

    if(!ev_press)    {        //如果未按下

        if(filp->f_flags & O_NONBLOCK)    //O_NONBLOCK:用户通过此指定不阻塞

            return –EAGAIN;

        else

            wait_event_interruptible(button_wait,ev_press);//阻塞在等待队列

}

ev_press = 0; //此行语句之前,ev_press其实是1的,中断程序里设置为1

                    //读取键值

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

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

}

 

3.中断处理程序

static irqreturn_t buttons_interrupt(int irq, void *dev_id){

        //读取按键gpio的数据寄存器的值

    down = !gpio_getpin(button_irqs->pin);//按下是值为0,而程序中1表示按下

        //key_values初始全是为0的,按下时变为1,则不再运行,可防止抖动

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

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

        ev_press = 1;        //有键按下

        wake_up_interruptible(&button_waitq);

}

return IRQ_RETVAL(IRQ_HANDLED);

}

 

 

原文地址:https://www.cnblogs.com/lihaiyan/p/4274461.html