【Linux】中断实践(一)

中断实践(一)

中断基础知识见:https://blog.csdn.net/DXflighting/article/details/111600973

实验内容:

自己虚拟出来一个设备,将该设备注册到系统中,执行自己写的中断服务例程。

我们要写一个中断内核模块,申请一个中断线,一个中断线对应一个IRQ号,虚拟出来的设备可以与其他中断线共用一条。

# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/interrupt.h>

static int irq;		//irq号			
static char * devname;		//设备名称	
			
//命令行中传入参数
module_param(irq,int,0644);
module_param(devname,charp,0644);
	
//用于共享IRQ中
struct myirq
{
    int devid;
};

struct myirq mydev={1119};
		
//中断处理函数,每执行一次count++
static irqreturn_t myirq_handler(int irq,void * dev)
{
    struct myirq mydev;
    static int count=1;
    mydev = *(struct myirq*)dev;		
    printk("key: %d..\n",count);
    printk("devid:%d ISR is working..\n",mydev.devid);
    printk("ISR is leaving......\n");
    count++;
    return IRQ_HANDLED;//代表接收到了准确的中断信号,并且做出了相应的正确的处理
}


//内核模块初始化函数
static int __init myirq_init(void)
{
    printk("Module is working...\n");
    //中断注册:request_irq(),flag是IRQF_SHARED表示共享
    if(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
    {
        printk("%s request IRQ:%d failed..\n",devname,irq);
        return -1;
    }
    printk("%s request IRQ:%d success...\n",devname,irq);
    return 0;
}

//内核模块退出函数
static void __exit myirq_exit(void)
{
    printk("Module is leaving...\n");
    free_irq(irq,&mydev);
    printk("Free the irq:%d..\n",irq);
}

MODULE_LICENSE("GPL");
module_init(myirq_init);
module_exit(myirq_exit);

插入内核,查看/proc/interrupt文件信息,是否将虚拟设备注册成功,并查看日志消息:
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述

程序分析(从内核分析):

程序的内核模块初始化函数中调用的内核中断注册函数:
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

request_irq函数的参数说明:

第1个参数irq:每一个IRQ中断线所对应的中断号。

第2个参数handler:是一个指针,指向处理这个中断实际中断处理程序,只要操作系统接收到中断,该函数就会被调用。该函数的返回值是irq_handler_t,该返回值的定义在/include/linux/irqreturn.h中:

enum irqreturn {
	IRQ_NONE,
	IRQ_HANDLED,
	IRQ_WAKE_THREAD,
};

typedef enum irqreturn irqreturn_t;

第3个参数flag:指定了快速中断或者中断共享中中断的处理属性,定义在inlcude/interrupt.h:

#define IRQF_DISABLED		0x00000020  //内核在处理中断程序期间,禁止所有的其他中断
#define IRQF_SAMPLE_RANDOM	0x00000040  
#define IRQF_SHARED		0x00000080     //可以在多个中断处理程序之间共享中断线
#define IRQF_PROBE_SHARED	0x00000100
#define IRQF_TIMER		0x00000200     //特别为系统定时器的中断处理而准备的
#define IRQF_PERCPU		0x00000400
#define IRQF_NOBALANCING	0x00000800
#define IRQF_IRQPOLL		0x00001000
#define IRQF_ONESHOT		0x00002000

第4个参数neme:与中断相关的设备的ASCII文本表示,例如PC机上键盘中断对应的这个值为“keyboard”,这些名字会被/proc/irq和/proc/interrupt文件使用。

第5个参数dev:用于共享中断线,当一个处理程序需要释放时,dev将提供唯一的标志信息(cookie),以便从共享中断线的诸多中断处理程序中删除指定的那一个。如果没有这个参数,那么内核不知道在给定的中断线上到底删除哪一个处理程序。

request_threaded_irq():

request_irq是函数是对request_threaded_irq函数的一个封装,该函数定义在/kernel/irq/manage.c

首先了解一下该函数涉及的两个个重要的结构体:

irqaction结构体主要用来存用户注册的中断处理函数,一个中断可以有多个处理函数,当一个中断有多个处理函数,说名这个是共享中断,所以在irq_desc结构体中的action成员是个链表,以action为表头,若是一个以上的链表就是共享中断。

struct irqaction {
         irq_handler_t handler;      //等于用户注册的中断处理函数,中断发生时就会运行这个中断处理函数
         unsigned long flags;         //中断标志,注册时设置,比如上升沿中断,下降沿中断等
         cpumask_t mask;           //中断掩码
         const char *name;          //中断名称,产生中断的硬件的名字
         void *dev_id;              //设备id
         struct irqaction *next;        //指向下一个成员
         int irq;                    //中断号,
         struct proc_dir_entry *dir;    //指向IRQn相关的/proc/irq/

};

irq_desc结构体中构造了很多项handle_irq函数,

struct irq_desc {
         irq_flow_handler_t       handle_irq;  //指向中断函数, 中断产生后,就会执行这个handle_irq
         struct irq_chip   *chip; //指向irq_chip结构体,用于底层的硬件访问,下面会介绍
         struct msi_desc             *msi_desc; 
         void                     *handler_data;  
         void                     *chip_data;
         struct irqaction     *action;      /* IRQ action list */   //action链表,用于中断处理函数
         unsigned int                  status;                  /* IRQ status */
         unsigned int                  depth;                  /* nested irq disables */
         unsigned int                  wake_depth;        /* nested wake enables */
         unsigned int                  irq_count;   /* For detecting broken IRQs */
         unsigned int                  irqs_unhandled;
         spinlock_t            lock;          
     ... ...
         const char            *name;              //产生中断的硬件名字
} ;

下面是request_threaded_irq()函数

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
			 irq_handler_t thread_fn, unsigned long irqflags,
			 const char *devname, void *dev_id)
{
	struct irqaction *action; //主要是用来存用户注册的中断处理函数,
	struct irq_desc *desc;//在irq_desc结构体中的action成员是个链表,以action为表头,若是一个以上的链表就是共享中断
	int retval;

	/*
	 * handle_IRQ_event() always ignores IRQF_DISABLED except for
	 * the _first_ irqaction (sigh).  That can cause oopsing, but
	 * the behavior is classified as "will not fix" so we need to
	 * start nudging drivers away from using that idiom.
	 */
	if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) ==
					(IRQF_SHARED|IRQF_DISABLED)) {
		pr_warning(
		  "IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQs\n",
			irq, devname);
	}

#ifdef CONFIG_LOCKDEP
	/*
	 * Lockdep wants atomic interrupt handlers:
	 */
	irqflags |= IRQF_DISABLED;
#endif
	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 */
	if ((irqflags & IRQF_SHARED) && !dev_id)
		return -EINVAL;

	desc = irq_to_desc(irq);//根据中断号,在irq_desc数组中返回一个具体的irq_desc
	if (!desc)
		return -EINVAL;

	if (desc->status & IRQ_NOREQUEST)
		return -EINVAL;

	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;
	}

	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;
//注册主要将我们传入的参数生成一个action,再添加到irq_desc上
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	chip_bus_lock(irq, desc);
	retval = __setup_irq(irq, desc, action);  //将生成的action添加到irq_desc上
	chip_bus_sync_unlock(irq, desc);

	if (retval)
		kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ
	if (!retval && (irqflags & IRQF_SHARED)) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}
EXPORT_SYMBOL(request_threaded_irq);

程序的内核模块退出函数中调用的内核中断注销函数:

void free_irq(unsigned int irq, void *dev_id)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (!desc)
		return;

	chip_bus_lock(irq, desc);
	kfree(__free_irq(irq, dev_id));
	chip_bus_sync_unlock(irq, desc);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值