Exynos4412 中断驱动开发(一)—— 中断基础及中断的注册过程

一、中断基础概念

        所谓中断,指CPU在执行程序的过程中,出现了某些突发事件即待处理,CPU必须暂停当前的程序。转去处理突发事件,处理完毕后CPU又返回原程序被中断的位置并继续执行。

1、中断分类

a -- 内部中断和外部中断

      根据中断的的来源,中断可以分为内部中断和外部中断:

内部中断,其中断源来自CPU内部(软件中断指令、溢出、除法错误等),例如,操作系统从用户态切换到内核态需借助CPU内部的软中断;

外部中断,其中断源来自CPU外部,由外设提出请求;

b -- 可屏蔽中断与不屏蔽中断

      根据中断是否可以屏蔽分为可屏蔽中断与不屏蔽中断:

可屏蔽中断,其可以通过屏蔽字被屏蔽,屏蔽后,该中断不再得到响应;

不屏蔽中断,其不能被屏蔽;

c -- 向量中断和非向量中断

     根据中断入口跳转方法的不同,分为向量中断和非向量中断:

向量中断,采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同的中断号有不同的入口地址;

非向量中断,其多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来标识具体是哪个中断。

    也就是说,向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务入口地址


2、中断ID

a -- IRQ number

      cpu给中断的一个编号,一个IRQ number是一个虚拟的interrupt ID,和硬件无关;

b -- HW interrupt ID

       对于中断控制器而言,它收集了多个外设的irq request line,要向cpu传递,GIC要对外设进行编码,GIC就用HW interrupt ID来标示外部中断;


3、SMP情况下中断两种形态

1-Nmode :只有一个processor处理器

N-N :所有的processor都是独立收到中断的

      GIC:SPI使用1-Nmode   PPI 和 sgi使用N-Nmode


二、中断编程

1、 申请IRQ

      在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:

int request_irq(unsigned int irq, irq_handler_t handler,
                         unsigned long irqflags, const char *devname, void *dev_id)

相关参数:

a -- irq是要申请的硬件中断号。另外,这里要思考的问题是,这个irq 是怎么得到的?这里我们在设备树中获取,具体解析见:

b -- handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。

c -- irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)

d -- devname设置中断名称,在cat /proc/interrupts中可以看到此名称。

e -- dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。

      request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。

      顶半部 handler 的类型 irq_handler_t 定义为:

typedef irqreturn_t (*irq_handler_t)(int, void *);

参数1:中断号
参数2 :参数
在Interrupt.h (e:\linux-3.14-fs4412\include\linux)    18323    2014/3/31中定义
IRQ_NONE       共享中断,如果不是我的设备产生的中断,就返回该值
IRQ_HANDLED     中断处理函数正确执行了就返回该值
IRQ_WAKE_THREAD        = (1 << 1)

    

2、释放IRQ

      与request_irq()相对应的函数为 free_irq(),free_irq()的原型为:

void free_irq(unsigned int irq, void *dev_id)

 free_irq()参数的定义与request_irq()相同。


三、中断注册过程分析

       我们每次用中断的时候就是注册一个中断函数。request_irq首先生成一个irqaction结构,其次根据中断号 找到irq_desc数组项(还记得吧,内核中irq_desc是一个数组,没一项对应一个中断号),然后将irqaction结构添加到 irq_desc中的action链表中。当然还做一些其他的工作,注册完成后,中断函数就可以发生并被处理了。

      irq_desc 内核中记录一个irq_desc的数组,数组的每一项对应一个中断或者一组中断使用同一个中断号,一句话irq_desc几乎记录所有中断相关的东西,这个结构是中断的核心。其中包括俩个重要的结构irq_chip 和irqaction 。

1、  irq_chip

      irq_chip  里面基本上是一些回调函数,其中大多用于操作底层硬件,设置寄存器,其中包括设置GPIO为中断输入就是其中的一个回调函数,分析一些源代码

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct irq_chip {  
  2.         const char     *name;  
  3.         unsigned int   (*startup)(unsigned int irq); //启动中断  
  4.         void           (*shutdown)(unsigned int irq); //关闭中断  
  5.         void           (*enable)(unsigned int irq);  // 使能中断  
  6.         void           (*disable)(unsigned int irq); // 禁止中断  
  7.    
  8.         void           (*ack)(unsigned int irq);   //中断应答函数,就是清除中断标识函数  
  9.         void           (*mask)(unsigned int irq);   //中断屏蔽函数  
  10.         void           (*mask_ack)(unsigned int irq); //屏蔽中断应答函数,一般用于电平触发方式,需要先屏蔽再应答  
  11.         void           (*unmask)(unsigned int irq);  //开启中断  
  12.         void           (*eoi)(unsigned int irq);  
  13.    
  14.         void           (*end)(unsigned int irq);  
  15.         int            (*set_affinity)(unsigned int irq,  
  16.                                       const struct cpumask *dest);  
  17.         int            (*retrigger)(unsigned int irq);  
  18.         int            (*set_type)(unsigned int irq, unsigned int flow_type); //设置中断类型,其中包括设置GPIO口为中断输入  
  19.         int            (*set_wake)(unsigned int irq, unsigned int on);  
  20.    
  21.         void           (*bus_lock)(unsigned int irq);  //上锁函数  
  22.         void           (*bus_sync_unlock)(unsigned int irq); //解锁  
  23.    
  24.         /* Currently used only by UML, might disappear one day.*/  
  25. #ifdef CONFIG_IRQ_RELEASE_METHOD  
  26.         void           (*release)(unsigned int irq, void *dev_id);  
  27. #endif  
  28.         /* 
  29.          * For compatibility, ->typename is copied into ->name. 
  30.          * Will disappear. 
  31.          */  
  32.         const char     *typename;  
  33. };  


       我们可以看到这里实现的是一个框架,需要我们进一步的填充里面的函数。我们在分析另一个结构irqaction

2、irqaction

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. include/linux/interrupt.h  
  2. struct irqaction {  
  3.         irq_handler_t handler;  //用户注册的中断处理函数  
  4.         unsigned long flags;    //中断标识  
  5.         const char *name;       //用户注册的中断名字,cat/proc/interrupts时可以看到  
  6.         void *dev_id;           //可以是用户传递的参数或者用来区分共享中断  
  7.         struct irqaction *next; //irqaction结构链,一个共享中断可以有多个中断处理函数  
  8.         int irq;                //中断号  
  9.         struct proc_dir_entry *dir;  
  10.         irq_handler_t thread_fn;  
  11.         struct task_struct *thread;  
  12.         unsigned long thread_flags;  
  13. };  
     

      我们用irq_request函数注册中断时,主要做俩个事情,根据中断号生成一个irqaction结构并添加到irq_desc中的 action结构链表,另一发面做一些初始化的工作,其中包括设置中断触发方式,设置一些irq_chip结构中没有初始化的函数为默认,开启中断,设置 GPIO口为中断输入模式(这里后面有详细流程分析)。

四、实例分析

        下面是一个按键中断驱动的编写,具体硬件分析在:Exynos4412 中断驱动开发(三)—— 设备树中中断节点的创建 

1、driver.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/device.h>  
  3. #include <linux/platform_device.h>  
  4. #include <linux/interrupt.h>  
  5. #include <linux/fs.h>  
  6. #include <linux/wait.h>  
  7. #include <linux/sched.h>  
  8. #include <asm/uaccess.h>  
  9. static int major = 250;  
  10.   
  11.   
  12. static wait_queue_head_t wq;  
  13. static int have_data = 0;  
  14. static int key;  
  15. static struct resource *res1;  
  16. static struct resource *res2;  
  17. static  irqreturn_t key_handler(int irqno, void *dev)  
  18. {  
  19. //  printk("key_handler irqno =%d \n",irqno);  
  20.     if(irqno == res1->start)  
  21.     {  
  22.         key = 1;  
  23.     }  
  24.     if(irqno == res2->start)  
  25.     {  
  26.         key = 2;  
  27.     }     
  28.     have_data = 1;  
  29.     wake_up_interruptible(&wq);  
  30.     return IRQ_HANDLED;  
  31. }  
  32. static int key_open (struct inode *inod, struct file *filep)  
  33. {  
  34.   
  35.     return 0;  
  36. }  
  37. static ssize_t key_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)  
  38. {  
  39.     wait_event_interruptible(wq, have_data==1);  
  40.     if(copy_to_user(buf,&key,sizeof(int)))  
  41.     {  
  42.         return -EFAULT;  
  43.     }  
  44.     have_data = 0;  
  45.     return len;  
  46. }  
  47. static  int key_release(struct inode *inode, struct file *filep)  
  48. {  
  49.     return 0;  
  50. }  
  51. static struct file_operations  key_ops =  
  52. {  
  53.     .open = key_open,  
  54.     .release = key_release,  
  55.     .read = key_read,  
  56. };  
  57.   
  58.   
  59. static int hello_probe(struct platform_device *pdev)  
  60. {  
  61.     int ret;  
  62.     printk("match 0k \n");  
  63.   
  64.     res1 = platform_get_resource(pdev,IORESOURCE_IRQ, 0);  
  65.     res2 = platform_get_resource(pdev,IORESOURCE_IRQ, 1);   
  66.          
  67.     ret = request_irq(res1->start,key_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED,"key1",NULL);  
  68.     ret = request_irq(res2->start,key_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED,"key2",NULL);  
  69.   
  70.     register_chrdev( major, "key", &key_ops);  
  71.   
  72.   
  73.     init_waitqueue_head(&wq);  
  74.       
  75.     return 0;  
  76. }  
  77. static int hello_remove(struct platform_device *pdev)  
  78. {  
  79.     free_irq(res1->start,NULL);  
  80.     free_irq(res2->start,NULL);    
  81.     unregister_chrdev( major, "key");  
  82.     return 0;  
  83. }  
  84.   
  85. static struct of_device_id key_id[]=  
  86. {  
  87.     {.compatible = "fs4412,key" },  
  88. };  
  89.   
  90. static struct platform_driver hello_driver=  
  91. {  
  92.       
  93.     .probe = hello_probe,  
  94.     .remove = hello_remove,  
  95.     .driver ={  
  96.         .name = "bigbang",  
  97.         .of_match_table = key_id,  
  98.     },  
  99. };  
  100.   
  101. static int hello_init(void)  
  102. {  
  103.     printk("hello_init");  
  104.     return platform_driver_register(&hello_driver);  
  105. }  
  106. static void hello_exit(void)  
  107. {  
  108.     platform_driver_unregister(&hello_driver);  
  109.     printk("hello_exit \n");  
  110.     return;  
  111. }  
  112. MODULE_LICENSE("GPL");  
  113. module_init(hello_init);  
  114. module_exit(hello_exit);  

test.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. main()  
  8. {  
  9.     int fd,len;  
  10.     int key;  
  11.     fd = open("/dev/hello",O_RDWR);  
  12.     if(fd<0)  
  13.     {  
  14.         perror("open fail \n");  
  15.         return ;  
  16.     }  
  17.   
  18.     while(1)  
  19.     {  
  20.         read(fd,&key,4);  
  21.         printf("============key%d==================\n",key);  
  22.     }  
  23.   
  24.     close(fd);  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值