前言
Linux4.0的内核,中断注册时(request_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)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
二、新版本注册方法
1.int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
函数没有变化。只是第一个参数发生了变化。第一个参数irq不再是硬中断号,而是linux内核软件中断号,这是内核管理的虚拟中断号。
2.硬件中断是我们通过device tree给出,然后通过函数platform_get_irq(dev, 0)获得软件中断号,再调用上面的注册函数进行中断注册。注册成功后,我们可以通过cat/proc/interrupts来查看我们注册的中断是否对应我们想要的中断号,这里面包含了硬中断号。
node = of_find_compatible_node(NULL,NULL,"itech,irq_test");
irq = of_irq_get(node,0);
上面2个函数一起使用也可以获取中断号,不过中断号一定要在device tree里正常加入。
3.设备树加入新的中断号。
irq_test@42 {
compatible = "itech,irq_test";
reg = <0xff200000 0x1000>;//use in test
interrupt-parent = <&intc>;
interrupts = <0 42 4>;
};
4.内核代码参考样例如下:
/*
* irq_test_device.c
* Author: root
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FIBER_UBUS_OFFSET (0x400)
//extern void * h2f_lwaxi_mem; //in bridge_remap.ko
struct miscdevice *misc_dev;
//int *fiber_ubus_base;
int irq;
static int irq_test_open(struct inode *inode, struct file *filp)
{
printk ("misc in irq_test_open\n");
return 0;
}
static int irq_test_release(struct inode *inode, struct file *filp)
{
printk ("misc irq_test_release\n");
return 0;
}
static ssize_t irq_test_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
{
int copied = 0;
printk ("misc irq_test_read\n");
return copied;
}
static ssize_t irq_test_write(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
{
int ret = size;
return ret;
}
static long irq_test_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
return ret;
}
static struct file_operations irq_test_fops =
{
.owner = THIS_MODULE,
.read = irq_test_read,
.write = irq_test_write,
.unlocked_ioctl = irq_test_ioctl,
.open = irq_test_open,
.release = irq_test_release,
};
static irqreturn_t irq_test_rx_irq_handler(int irq,void *id) //
{
printk("in kernel interrupt\n");
return IRQ_RETVAL(IRQ_HANDLED);
}
static int irq_test_probe(struct platform_device *dev)
{
struct device_node *node;
struct resource *res;
printk("in irq test device probe\n");
*(fiber_ubus_base + 3) = 0x0;
irq = platform_get_irq(dev, 0);
if (irq < 0) {
dev_err(&dev->dev, "no irq resource?\n");
printk("irq= %d \n", irq);
return irq;
}
printk("irq= %d \n", irq);
misc_dev = (struct miscdevice *)kzalloc(sizeof(*misc_dev), GFP_KERNEL);
misc_dev->fops = &irq_test_fops;
misc_dev->name = "irq_test-test";
//主设备号恒为10,自动分配次设备号
misc_dev->minor = MISC_DYNAMIC_MINOR;
//3.注册misc设备
misc_register(misc_dev);
if(request_irq(irq,(irq_handler_t)irq_test_rx_irq_handler, IRQF_TRIGGER_HIGH, "irq_test" , misc_dev->this_device) )
{
printk("requst irq failled \n");
}
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
printk("mem start is 0x%x\n",res->start);
printk("mem end is 0x%x\n",res->end);
msleep(1);
return 0;
}
static int irq_test_remove(struct platform_device *dev)
{
printk("in irq test device remove\n");
// *(fiber_ubus_base + 3) = 0x0;
free_irq(irq,misc_dev->this_device);
misc_deregister(misc_dev);
kfree(misc_dev);
return 0;
}
struct of_device_id dts_table[] = {
{.compatible = "itech,irq_test",},//the name must same int device tree
{},
};
struct platform_driver test_pl = {
.probe = irq_test_probe,
.remove = irq_test_remove,
.driver = {
.name = "irq_test_test_driver",
.of_match_table = dts_table,
},
};
static int __init test_irq_test_init(void)
{
printk("insmod test irq\n");
platform_driver_register(&test_pl);
return 0;
}
/**
* nufront_cap_cleanup - Driver un-registration call
*/
static void __exit test_irq_test_cleanup(void)
{
printk("rmmod test irq\n");
platform_driver_unregister(&test_pl);
}
module_init(test_irq_test_init);
module_exit(test_irq_test_cleanup);
MODULE_AUTHOR("itech_han");
MODULE_LICENSE("GPL");
MODULE_ALIAS("irq_test");