linux驱动学习(九)之中断

一、中断的实现

对于中断的请求,在linux内核中,有一套标准的接口函数,可以实现中断的响应和处理。

#include <linux/interrupt.h>

//1 申请中断
static inline int __must_check request_irq(unsigned int irq, 
										irq_handler_t handler, 
										unsigned long flags, 
										const char *name, 
										void *dev)

1)unsigned int irq  ---->中断号,每个中断源有一个唯一中断号
2)irq_handler_t handler  --->中断请求发生后,被调用的中断服务函数 typedef irqreturn_t (*irq_handler_t)(int, void *); 

/**
 * enum irqreturn
 * @IRQ_NONE		interrupt was not from this device  中断发生异常时
 * @IRQ_HANDLED		interrupt was handled by this device 正常返回
 * @IRQ_WAKE_THREAD	handler requests to wake the handler thread
 */
enum irqreturn {
	IRQ_NONE		= (0 << 0),
	IRQ_HANDLED		= (1 << 0),
	IRQ_WAKE_THREAD		= (1 << 1),
};

typedef enum irqreturn irqreturn_t;

3)unsigned long flags ---->中断的标志----->中断触发方式
4) const char *name --->中断的名称,是由用户自定义
5)void *dev ---->向中断服务程序传递的参数 

返回值:
   成功:0
   失败:返回负数的错误码

查看开发板linux系统中的中断:

[root@GEC6818 /proc]#cat interrupts 

	   CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       
 33:          0          0          0          0          0          0          0          0       GIC  pl08xdmac
 34:          0          0          0          0          0          0          0          0       GIC  pl08xdmac
 37:          0          0          0          0          0          0          0          0       GIC  rtc 1hz
 39:         18         14         13         10         22         14         14         29       GIC  nxp-uart
 48:          0          0          0          1          0          0          0          0       GIC  s3c2440-i2c.1
 49:       3197        153       3935       3803       3735       3151       3387       3896       GIC  s3c2440-i2c.2
 56:      22946      15098      13068      14422      15060      15512      15454      18817       GIC  Event Timer IRQ
 63:          0          0          0          0          0          0          0          0       GIC  nxp-wdt
 65:       1402        876        712        851        786        901        919        982       GIC  nxp-disp
 66:          0          0          0          0          0          0          0          0       GIC  nxp-disp
 68:          0          0          1          0          0          0          0          0       GIC  hdmi-int
 中断号       中断被处理的次数                                                                    中断控制器   中断名称

在linux内核中,如果中断号被使用或者被其他申请了,再申请就会出错:

[root@GEC6818 /6818_driver]#insmod led_drv.ko 
[ 1022.779000] gec6818_interrupt_init
[ 1022.779000] ------------[ cut here ]------------
[ 1022.782000] WARNING: at kernel/irq/manage.c:1370 request_threaded_irq+0x114/0x13c()
[ 1022.789000] Modules linked in: led_drv(O+)
[ 1022.793000] [<c001517c>] (unwind_backtrace+0x0/0x134) from [<c0044880>] (warn_slowpath_common+0x54/0x64)
[ 1022.803000] [<c0044880>] (warn_slowpath_common+0x54/0x64) from [<c004492c>] (warn_slowpath_null+0x1c/0x24)
[ 1022.813000] [<c004492c>] (warn_slowpath_null+0x1c/0x24) from [<c00b8fd8>] (request_threaded_irq+0x114/0x13c)
[ 1022.822000] [<c00b8fd8>] (request_threaded_irq+0x114/0x13c) from [<bf00203c>] (gec6818_interrupt_init+0x3c/0x50 [led_drv])
[ 1022.833000] [<bf00203c>] (gec6818_interrupt_init+0x3c/0x50 [led_drv]) from [<c0008710>] (do_one_initcall+0x11c/0x174)
[ 1022.844000] [<c0008710>] (do_one_initcall+0x11c/0x174) from [<c0097dc0>] (sys_init_module+0xaa4/0x1e14)
[ 1022.853000] [<c0097dc0>] (sys_init_module+0xaa4/0x1e14) from [<c000ec40>] (ret_fast_syscall+0x0/0x30)
[ 1022.863000] ---[ end trace d4f7d5ddfdae4527 ]---
insmod: can't insert 'led_drv.ko': invalid parameter  ---->不合法参数,request_irq的参数不对

成功输出:

[root@GEC6818 /6818_driver]#insmod led_drv.ko 
[ 2711.551000] gec6818_interrupt_init
[root@GEC6818 /6818_driver]#lsmod
led_drv 977 0 - Live 0xbf008000 (O)
[root@GEC6818 /6818_driver]#cat /proc/interrupts 
134:          0          0          0          0          0          0          0          0      GPIO  my_interrupt
134 --->中断号 IRQ_GPIO_A_START + 28 --->106 + 28 --->134
//2 释放中断
void free_irq(unsigned int, void *)
参数说明:
	unsigned int ---->要释放的中断的中断号
	void * ----> 中断服务程序的参数

二、中断号

unsigned int irq

中断号是跟中断源有关,每一个中断源有一个唯一中断号,中断源由外部与内部,中断源是跟硬件有关,每一个处理器平台,中断源不一样的。

1、硬件平台的中断源的定义

kernel\arch\arm\mach-s5p6818\include\mach\s5p6818_irq.h
/*
* Physical Interrupt Number 64 (0~63)
*/
#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

/*
 * GPIO Interrupt Number 160 (106~265)
 */
#define IRQ_GPIO_START			IRQ_PHY_MAX_COUNT
#define IRQ_GPIO_END			(IRQ_GPIO_START + 32 * 5)	// Group: A,B,C,D,E

#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)	

关于中断号与GPIO:   KEY2->GPIOA28 
 按键       中断号                     GPIO口号        GPIO口
KEY2    IRQ_GPIO_A_START+28         PAD_GPIO_A+28   GPIOA28

头文件:#include <cfg_type.h>

三、中断标志

unsigned long flags

注意:中断一旦安装成功(request_irq),默认是打开的

中断标志分为:外部中断和内部中断


1、外部中断标志

#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

双边沿触发:IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING    

2、内核内部中断

#define IRQF_DISABLED	0x00000020   //当响应当前中断的时候,关闭其他的中断
#define IRQF_SHARED		0x00000080  	 //当一个中断源,注册多次时,可以对应多个中断服务程序
#define IRQF_TIMER		(__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)  //当注册定时器中断时,使用该标志

四、中断服务程序

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

通常情况下,一个中断源对应一个中断服务程序(中断处理函数),也可以多个中断源对应一个中断服务程序。

irqreturn_t key_irq_handler(int irq,void* dev)
{
  
}

1 根据中断号来区分中断处理
2 通过中断请求传递的参数来区分中断处理
头文件:#include <linux/interrupt.h>

注意:对于嵌入式系统中,中断服务程序是一个原子操作,所谓的原子操作就是该过程被执行时,不能被打断。 在中断服务服务程序中,不能有阻塞的操作,如睡眠,copy_to_user,copy_from_user,锁等。

在linux内核中,使用睡眠函数要包含头文件:

#include <linux/delay.h>
static inline void ssleep(unsigned int seconds)  ---->秒级睡眠
void msleep(unsigned int msecs); ----->毫秒

五、中断与等待队列(wait_queue)

 1、等待队列

设置一个等待条件,如果条件满足,则进程继续往下执行;如果条件不满足,则进程就进入等待队列中。当条件满足时,中断会唤醒等待队列中的进程,进程再继续往下执行。等待队列也是一种同步方法。

2、等待队列的相关API接口函数

#include <linux/wait.h>
#include <linux/sched.h>

//等待队列结构体:
typedef struct __wait_queue wait_queue_t;
struct __wait_queue 
{
	unsigned int flags;  //条件变量的标志
	#define WQ_FLAG_EXCLUSIVE	0x01
	void *private;
	wait_queue_func_t func;
	struct list_head task_list;
};

1)定义一个等待队列 和等待的条件
static wait_queue_t gec6818_wait;
static in key_press_flag = 0;

2)初始化等待队列
#define init_wait(wait)							\
	do {								\
	(wait)->private = current;				\
	(wait)->func = autoremove_wake_function;		\
	INIT_LIST_HEAD(&(wait)->task_list);			\
	(wait)->flags = 0;					\
} while (0)
static inline void INIT_LIST_HEAD(struct list_head *list)

由此可得:
void init_wait(wait_queue_t* wait);
如 init_wait(&gec6818_wait);

#define init_waitqueue_head(q)				\
	do {						\
	static struct lock_class_key __key;	\
	\
	__init_waitqueue_head((q), #q, &__key);	\
} while (0)

得到原型:void init_waitqueue_head(wait_queue_head_t*);

struct __wait_queue_head 
{
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
比如:wait_queue_head_t gec6818_wq_head;

3)进入等待队列
#define wait_event_interruptible(wq, condition)				\
	({									\
	int __ret = 0;							\
	if (!(condition))						\
	__wait_event_interruptible(wq, condition, __ret);	\
	__ret;								\
})
struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};

typedef struct __wait_queue_head wait_queue_head_t;
void wait_event_interruptible(wait_queue_head_t wait,int condition)

4)唤醒队列
#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)

void wake_up(wait_queue_head_t* head)

觉得有帮助的话,打赏一下呗。。

           

相关推荐

  1. Linux驱动开发——(五)内核中断

    2024-06-15 08:22:01       25 阅读
  2. Linux内核--设备驱动()WIFI

    2024-06-15 08:22:01       37 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-15 08:22:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-15 08:22:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-06-15 08:22:01       82 阅读
  4. Python语言-面向对象

    2024-06-15 08:22:01       91 阅读

热门阅读

  1. 深入理解 MySQL 查询分析工具 EXPLAIN 的使用

    2024-06-15 08:22:01       35 阅读
  2. Python实现电脑自动关机

    2024-06-15 08:22:01       21 阅读
  3. Ubuntu下ClickHouse安装

    2024-06-15 08:22:01       31 阅读
  4. Centos7.9 安装 clickhouse 24.6.1

    2024-06-15 08:22:01       21 阅读
  5. Scala入门教程

    2024-06-15 08:22:01       26 阅读
  6. volatile关键字

    2024-06-15 08:22:01       35 阅读
  7. Python中的多态和封装是如何实现的

    2024-06-15 08:22:01       34 阅读
  8. React小记——如何理解 props 单向数据流?

    2024-06-15 08:22:01       30 阅读
  9. 【个人博客搭建】(24)统一api接口返回格式

    2024-06-15 08:22:01       33 阅读
  10. 使用 Selenium 自动化获取 CSDN 博客资源列表

    2024-06-15 08:22:01       31 阅读