九、【interrupt】按键中断

一、linux内核中中断的使用

1、申请中断

static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) 

参数:

  • irq: 中断号
  • handler:中断处理程序
  • flags:中断标志,

对应外部中断,flags表示中断触发的方式:

#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008 

对于其他一般中断,flags用于设置中断的处理方式:

#define IRQF_DISABLED		0x00000020
#define IRQF_SAMPLE_RANDOM	0x00000040
#define IRQF_SHARED		0x00000080
  • name:自定义的中断名。
  • dev:传递给中断处理程序的实参。

返回值:

  成功返回0,否则返回负值。

2、中断处理程序

irqreturn_t xxx_handler(int irq, void * dev 

参数:

  • irq:中断号。
  • dev:中断申请函数传过来的参数。

返回值:

  返回值为以下三个枚举值,IR_HANDLED表示中断被正确处理了

enum irqreturn {
  IRQ_NONE = (0 << 0),
  IRQ_HANDLED = (1 << 0),
  IRQ_WAKE_THREAD = (1 << 1),
};

3、中断注销

void free_irq(unsigned int irq, void *dev_id) 

参数

  • irq:中断号。
  • dev_id:中断的结构体

 

二、查看中断

 

三、中断号

中断号其实就是一个整数,该整数是内核给每个中断源的唯一编号

6818主板的中断号定义在s5p6818_irq.h中

1、外部中断号

#define IRQ_GPIO_A_START		(IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START		(IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START		(IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START		(IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START		(IRQ_GPIO_START + PAD_GPIO_E)

2、其他一般中断的中断号

#define IRQ_PHY_MCUSTOP					(0  + 32)
#define IRQ_PHY_DMA0					(1  + 32)
#define IRQ_PHY_DMA1					(2  + 32)
#define IRQ_PHY_CLKPWR_INTREQPWR		(3  + 32)
#define IRQ_PHY_CLKPWR_ALIVEIRQ			(4  + 32)
#define IRQ_PHY_CLKPWR_RTCIRQ			(5  + 32)
#define IRQ_PHY_UART1					(6	+ 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0					(7	+ 32) // UART0_MODULE
#define IRQ_PHY_UART2					(8	+ 32) // UART1_MODULE
#define IRQ_PHY_UART3					(9	+ 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4					(10 + 32)	// pl01115_Uart_nodma1
#define IRQ_PHY_UART5					(11 + 32)	// pl01115_Uart_nodma2
#define IRQ_PHY_SSP0					(12 + 32)
#define IRQ_PHY_SSP1					(13 + 32)
#define IRQ_PHY_SSP2					(14 + 32)
#define IRQ_PHY_I2C0					(15 + 32)
#define IRQ_PHY_I2C1					(16 + 32)
#define IRQ_PHY_I2C2					(17 + 32)
#define IRQ_PHY_DEINTERLACE				(18 + 32)
#define IRQ_PHY_SCALER					(19 + 32)
#define IRQ_PHY_AC97					(20 + 32)
#define IRQ_PHY_SPDIFRX					(21 + 32)
#define IRQ_PHY_SPDIFTX					(22 + 32)
#define IRQ_PHY_TIMER_INT0				(23 + 32)
#define IRQ_PHY_TIMER_INT1				(24 + 32)
#define IRQ_PHY_TIMER_INT2				(25 + 32)
#define IRQ_PHY_TIMER_INT3				(26 + 32)
#define IRQ_PHY_PWM_INT0				(27 + 32)
#define IRQ_PHY_PWM_INT1				(28 + 32)
#define IRQ_PHY_PWM_INT2				(29 + 32)
#define IRQ_PHY_PWM_INT3				(30 + 32)
#define IRQ_PHY_WDT						(31 + 32)
#define IRQ_PHY_MPEGTSI					(32 + 32)
#define IRQ_PHY_DPC_P					(33 + 32)
#define IRQ_PHY_DPC_S					(34 + 32)
#define IRQ_PHY_RESCONV					(35 + 32)
#define IRQ_PHY_HDMI					(36 + 32)
#define IRQ_PHY_VIP0					(37 + 32)
#define IRQ_PHY_VIP1					(38 + 32)
#define IRQ_PHY_MIPI					(39 + 32)
#define IRQ_PHY_VR						(40 + 32)
#define IRQ_PHY_ADC						(41 + 32)
#define IRQ_PHY_PPM						(42 + 32)
#define IRQ_PHY_SDMMC0					(43 + 32)
#define IRQ_PHY_SDMMC1					(44 + 32)
#define IRQ_PHY_SDMMC2					(45 + 32)
#define IRQ_PHY_CODA960_HOST			(46 + 32)
#define IRQ_PHY_CODA960_JPG				(47 + 32)
#define IRQ_PHY_GMAC					(48 + 32)
#define IRQ_PHY_USB20OTG				(49 + 32)
#define IRQ_PHY_USB20HOST				(50 + 32)
#define IRQ_PHY_CAN0					(51 + 32)
#define IRQ_PHY_CAN1					(52 + 32)
#define IRQ_PHY_GPIOA					(53 + 32)
#define IRQ_PHY_GPIOB					(54 + 32)
#define IRQ_PHY_GPIOC					(55 + 32)
#define IRQ_PHY_GPIOD					(56 + 32)
#define IRQ_PHY_GPIOE					(57 + 32)
#define IRQ_PHY_CRYPTO					(58 + 32)
#define IRQ_PHY_PDM						(59 + 32)
#define IRQ_PHY_TMU0                    (60 + 32)
#define IRQ_PHY_TMU1                    (61 + 32)
#define IRQ_PHY_VIP2					(72 + 32)

3、三个中断共用一个中断处理程序

如何区分是哪个中断发生导致中断处理程序被内核调用的?
(1)通过中断号----某个中断发生,该中断的中断号会传递给中断处理程序的第一个形参
(2)通过注册时,传递给中断处理程序不同 (void * dev)

4、中断处理程序是一个原子过程

中断处理过程是原子的不能在中断处理程序中使用可能导致阻塞的函数,例如:

ssleep 、copy_to_user/copy_from_user、获取信号量.. 

不然会导致如下错误:

[   39.085000] Exception stack(0xc0a93f58 to 0xc0a93fa0)
[   39.085000] 3f40:                                                       ffffffed 00000000
[   39.085000] 3f60: 00000000 00000000 c0a92000 c0b2c248 c0738290 c0a92000 c0a92000 c0ab0128
[   39.085000] 3f80: 00000000 00000000 00000019 c0a93fa0 c000f83c c000f840 600d0013 ffffffff
[   39.085000] [<c000e840>] (__irq_svc+0x40/0x70) from [<c000f840>] (default_idle+0x2c/0x30)
[   39.085000] [<c000f840>] (default_idle+0x2c/0x30) from [<c000fb90>] (cpu_idle+0xd8/0x104)
[   39.085000] [<c000fb90>] (cpu_idle+0xd8/0x104) from [<c0a5483c>] (start_kernel+0x328/0x334)
[   39.085000] ---[ end trace 340d61c57173f93b ]---
[   39.085000] BUG: scheduling while atomic: swapper/0/0/0x00010002---------在原子过程中启动了调度器

内核中提供的延时函数:以下的函数是忙等延时,不会休眠。ssleep是休眠延时,我们中断处理程序中不能使用忙等延时。

udelay()
ndelay() 

 

四、按键驱动程序

key_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
struct key_gpio_t{
	unsigned int irq;
	char irqname[20];
	unsigned char keyvalue;
};


static struct key_gpio_t key_gpio[]=
{
	{IRQ_GPIO_A_START+28,"KEY2_GPIOA28",2},
	{IRQ_GPIO_B_START+30,"KEY3_GPIOB30",3},
	{IRQ_GPIO_B_START+31,"KEY4_GPIOB31",4},
	{IRQ_GPIO_B_START+9,  "KEY6_GPIOB9",6},
};
static char keyvalue = 0;
ssize_t key_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
{
	   
       int ret;
	  if(size !=1)
	  {
	  	return -EINVAL;
	  }
	  ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
	  if(ret != 0)
	  {
	  	return (size -ret);
	  }
	  keyvalue=0;
	  return size;
}
static irqreturn_t key_handler(int irq, void * dev)
{
   	
	struct key_gpio_t keytmp=*(struct key_gpio_t *)dev;
	keyvalue =keytmp.keyvalue;
	mdelay(500);  //按键防抖
	return IRQ_HANDLED;
}	

struct file_operations key_misc_fops=
{
	.read = key_read,
};
static struct miscdevice key_misc={
	.minor = MISC_DYNAMIC_MINOR,
	.name = "key_misc",
	.fops =  &key_misc_fops,
	};
static int __init  key_init(void)
{
      int ret,i;
       printk(KERN_INFO"key_init\n");
      ret = misc_register(&key_misc);
      if(ret < 0)
      {
      		printk(KERN_INFO"key misc register fail.\n");
		goto misc_register_err;	
      }
	for(i=0;i<4;i++)
	{
		 ret = request_irq(key_gpio[i].irq, key_handler,IRQF_TRIGGER_FALLING,key_gpio[i].irqname,(void*)&key_gpio[i]);
		 if(ret < 0)
		 {
		 	printk(KERN_INFO"request_irq fail.\n");
			goto irq_request_err;
		 }
	}
	return 0;

irq_request_err:
	while(i--) 
	{
		free_irq(key_gpio[i].irq,NULL);
	}
misc_register_err:
		return 0;
}

static void __exit key_exit(void)
{
	int i;
    printk(KERN_INFO"key_exit\n");
    misc_deregister(&key_misc);	  
      for(i=0;i<4;i++) 
     {
		free_irq(key_gpio[i].irq,(void *)&key_gpio[i]);
     }
  
}

module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");

  

 

main.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>


int main()
{
    int fd,ret;

    char keyvalue=0;
    fd = open("/dev/key_misc",O_RDWR);
    if(fd<0)
    {
        perror("open key_misc error!");        
    }
    while(1)
    {
        ret=read(fd,&keyvalue,1);
        if(ret !=1)
        {
            perror("read error");
            continue;
        }
        if(keyvalue!=0)
        {
             printf("keyvalue=%d\n",keyvalue);
        }
    }
    
     close(fd);
} 

  

 相关链接:

    Linux设备树语法详解-中断

 

  

 

  

 

  

  

 

原文地址:https://www.cnblogs.com/yuanqiangfei/p/15706823.html