Linux设备中断程序编写

来源:互联网 发布:美国在台协会 知乎 编辑:程序博客网 时间:2024/06/07 06:02

一. 几个要点知识:

    1. 睡眠

        安全睡眠的要点:

        <1> 当你运行在原子上下文时不能睡眠;

        <2> 醒后必须通过检查来确你在等待的条件;

        <3> 睡眠必须对应一个唤醒。

 

    <注>: 代码针对于2.6的内核

    等待队列:   DECLARE_WAIT_QUEUE_HEAD(name);

    睡眠函数:   wait_event_interruptible (queue, condition)

                     (它可能被信号中断. 这个版本返回一个你应当检查的整数值; 一个非零值意味着你的睡眠被某些信号打断, 并且你的驱动可能当

                      返回 -ERESTARTSYS.)

    唤醒函数:  void wake_up_interruptible(wait_queue_head_t *queue);

 

    2. poll, select

    获取设备驱动的支持:  unsigned int (*poll) (struct file *filp, poll_table *wait);

        <1>  在一个或多个可指示查询状态变化的等待队列上调用 poll_wait

                void poll_wait (struct file *, wait_queue_head_t *, poll_table *)
        <2>  返回一个位掩码, 描述可能不必阻塞就立刻进行的操作.标志参见<linux/poll.h>

 

    3. 中断

      中断注册函数: int request_irq(unsigned int irq,
                                                     irqreturn_t (*handler)(int, void *, struct pt_regs *),
                                                     unsigned long flags,
                                                     const char *dev_name,
                                                     void *dev_id);

      中断释放函数: void free_irq(unsigned int irq, void *dev_id);
            request_irq 返回给请求函数的返回值或者是 0 指示成功, 或者是一个负的错误码, 如同平常. 函数返回 -EBUSY 来指示另一个驱

      动已经使用请求的中断线是不寻常的. 函数的参数如下:
      unsigned int irq            请求的中断号
      irqreturn_t (*handler)  安装的处理函数指针
      unsigned long flags      如你会希望的, 一个与中断管理相关的选项的位掩码(后面描述).
      const char *dev_name 这个传递给 request_irq 的字串用在 /proc/interrupts 来显示中断的拥有者
      void *dev_id                 用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有

                                           数据区(来标识哪个设备在中断).如果中断没有被共享, dev_id 可以设置为 NULL。

 

 

      调用 request_irq 的正确位置是当设备第一次打开时, 在硬件被指示来产生中断前.

      调用 free_irq 的位置是设备最后一次被关闭时, 在硬件被告知不要再中断处理器之后.

     

      中断处理函数: irqreturn_t short_interrupt(int irq, void *dev_id, struct pt_regs *regs)

                            struct pt_regs *regs, 很少用到

 

二. TQ2440 irq 代码分析

     1.驱动代码(不完整)

              天嵌提供的实例代码并不复杂,但作为一个例程是非常合适的,代码具体的给出了如何注册,处理,释放中断。

 

              /*必要的头文件,我只列出中断相关的*/

                      ....

              #include <linux/poll.h>
          #include <linux/irq.h>
          #include <asm/irq.h>
          #include <linux/interrupt.h>
                       ....
         
         

               #define  DEVICE_NAME   "EmbedSky_buttons"
         
          #define  BUTTON_MAJOR  232
                                 //设备号用自动获取方式更好!
    
              /*等待队列*/
          static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

             

              /*中断flag, 中断处理函数置1,read函数清0 */
          static volatile int ev_press = 0;
  
           /* 按钮被按下的回数(准确地说,是发生中断的回数) */
          static volatile int press_cnt[] = {0,0,0,0};

             

              static struct class *button_class;

             

             /*中断信息结构,这个结构的设计很不错*/

              struct button_irqs_desc {                                                        
                                                   int irq;

                                                       int pin;

                                                   int pin_setting;

                                                       int number;
                                                   char *name;

                                                     };
  
  
          static struct button_irqs_desc button_irqs[] =

              {

                  {IRQ_EINT1,  S3C2410_GPF1, S3C2410_GPF1_ENT1, 0, "KEY1"},

                  {IRQ_EINT4,  S3C2410_GPF4, S3C2410_GPF4_ENT4, 1, "KEY2"},

                  {IRQ_EINT2,  S3C2410_GPF2, S3C2410_GPF2_ENT2, 2, "KEY3"},

                  {IRQ_EINT0,  S3C2410_GPF0, S3C2410_GPF0_ENT0, 3, "KEY4"},
          };
  

            /*中断处理函数*/ 
             static irqreturn_t buttons_interrupt(int irq,void *dev_id)
          {
                 //读键值(略)
                 ev_press = 1;                                          /*表示中断发生了*/
                 wake_up_interruptible(&button_waitq); /*唤醒休眠的进程*/
                 return IRQ_RETVAL(IRQ_HANDLED);
           }
       

               static int tq2440_buttons_open(struct inode *inode,structfile *file)
           {
                 int i;
                 int err;
               

                     //中断注册
                 for(i =0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)

                     {

                           s3c2410_gpio+cfgpin( buttons_irqs[i].pin, buttons_irqs[i]_pin_setting );
                       err = request_irq ( buttons_irqs[i].irq, buttons_interrupt, NULL,                                                           buttons_irqs[i].name, (void *)&buttons_irqs[i]);
  
                       if(err)
                            break;
                  }
                

                       if(err)

                      {
                        i--;
                        for( ;i>=0;i-- )

                            free_irq(buttons_irqs[i].irq,(void *)&buttons_irq);
                        return -EBUSY;
                   }
  

                    return 0;
          }
      

              static int tq2440_buttons_close(struct inode *inode,structfile *file)
         {
                 int i;
              

                    //中断释放

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

                    {
                       free_irq(button_irqs[i].irq,(void *)&buttons_irq]);
                }
              

                     return 0;
             }
  
           static int tq2440_buttons_read(struct file *filp,char__user *buff,size_t count,loff_t *offp)
          {
                unsigned long err;
                

                    if(!ev_press)

                    { 

                         if()

                         else 

                               wait_event_interruptible(button_waitq,ev_press);

                     }
               

                     ev_press = 0;
               

                     err = copy_to_user(buff,(const void*)press_cnt,MIN(sizeof(press_cnt),count));
                 memset((void *)press_cnt,0,sizeof(press_cnt));
  

                     return err? -EFAULT:0;
            }

              

                static unsigned int EmbedSky_buttons_poll(struct file *filp, poll_table *wait)

               {

                      ....

                      poll_wait(...);

                     

                      return mask; 

               }
      

                static struct file_operations tq2440_buttons_fops =

               {
               .owner   =   THIS_MODULE,

                   .open     =   tq2440_buttons_open,
               .release  =   tq2440_buttons_close,
               .read      =   tq2440_buttons_read,
            };
       

                static int __init tq2440_buttons_init(void)
            {
                     ........

            }

 

                static void __exit tq2440_buttons_exit(void)
            {

                     ........
            }

              module_init(tq2440_buttons_init);
            module_exit(tq2440_buttons_exit)
 
 

原创粉丝点击