中断顶半部:不允许耗时操作
代码流程:
1、基于字符设备驱动的注册(手动/自动)
2、基于设备树文件的自定义完成(myled, myirq)
2、基于GPIO子系统实现led的点亮(流水/测试文件控制)
3、中断子系统操作流程
3.1根据路径解析设备树节点信息
3.2根据设备树节点指针解析获得软中断号
API: irq_of_parse_and_map
3.3自定义中断处理函数(其内部完成对led灯的亮灭操作)
API: irqreturn_t irq_handler(int irqno, void *dev)
3.4注册要使用的中断号进入内核
API: request_irq
3.5注销软中断号
API: free_irq
注:循环注册需使用到irq_request的最后一个参数(void *),且循环注册需要循环注销
代码示例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#define MAX 3
struct device_node *node;
struct device_node *node2;
struct gpio_desc *desc1;
struct gpio_desc *desc2;
struct gpio_desc *desc3;
unsigned int irqno[3];
char* irqname[3] = {"key1", "key2", "key3"};
//int gpiono;
int major;
char kbuf[128]={0};
//中断处理函数
irqreturn_t irq_handler(int irqno, void *dev)
{
int i = (int *)dev;
switch(i)
{
case 0:
gpiod_set_value(desc1, 0);
break;
case 1:
gpiod_set_value(desc2, 0);
break;
case 2:
gpiod_set_value(desc3, 0);
break;
}
return IRQ_HANDLED;//表示当前中断被处理了
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *off)
{
int ret;
if(size>sizeof(kbuf)) //size传过来的是sizeof(ubuf),如果ubuf>kbuf,那么实际长度为kbuf
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if(ret)
{
printk("copy to user failed...\n");
return -EIO;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *off)
{
int ret;
if(size>sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if(ret)
{
printk("copy from user failed...\n");
return -EIO;
}
switch(kbuf[0])
{
/*
case '0':
gpiod_set_value(desc1, 0);
break;
*/
case '1':
gpiod_set_value(desc1, 1);
break;
/*
case '2':
gpiod_set_value(desc2, 0);
break;
*/
case '3':
gpiod_set_value(desc2, 1);
break;
/*
case '4':
gpiod_set_value(desc3, 0);
break;
*/
case '5':
gpiod_set_value(desc3, 1);
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops={
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int ret,i;
major = register_chrdev(0, "myled", &fops);
if(major<0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功,major=%d\n", major);
//根据路径解析设备树节点信息
node = of_find_node_by_path("/myleds");
if(node == NULL)
{
printk("of_find_node_by_path failed...\n");
return -1;
}
//根据路径解析设备树节点信息
node2 = of_find_node_by_path("/myirq");
if(node2 == NULL)
{
printk("of_find_node_by_path failed...\n");
return -1;
}
/*
//老版
//根据设备树结构体信息解析GPIO编号
gpiono = of_get_named_gpio(node, "led1", 0);
if(gpiono < 0)
{
printk("of_get_named_gpio failed...\n");
return -1;
}
//注册GPIO编号
ret = gpio_request(gpiono, NULL);
if(ret)
{
printk("gpio_request failed...\n");
return -1;
}
//设置当前GPIO方向为输出
gpio_direction_output(gpiono, 0);
//点灯
gpio_set_value(gpiono, 1);
*/
//根据设备树节点信息解析出软中断号
for(i=0;i<MAX;i++)
{
irqno[i]=irq_of_parse_and_map(node2,i);
if(irqno[i]==0)
{
printk("irq_of_parse_and_map failed...\n");
return -ENOENT;
}
//注册中断号进内核
ret=request_irq(irqno[i],irq_handler,IRQF_TRIGGER_FALLING,irqname[i],(void *)i);
if(ret)
{
printk("request_irq failed...\n");
return ret;
}
}
//新版
//led1,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
desc1 = gpiod_get_from_of_node(node, "led1", 0, GPIOD_OUT_LOW, NULL);
if(IS_ERR(desc1))
{
printk("#desc1 gpiod_get_from_of_node failed...\n");
return PTR_ERR(desc1);
}
//led2,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
desc2 = gpiod_get_from_of_node(node, "led2", 0, GPIOD_OUT_LOW, NULL);
if(IS_ERR(desc2))
{
printk("#desc2 gpiod_get_from_of_node failed...\n");
return PTR_ERR(desc2);
}
//led3,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
desc3 = gpiod_get_from_of_node(node, "led3", 0, GPIOD_OUT_LOW, NULL);
if(IS_ERR(desc3))
{
printk("#desc3 gpiod_get_from_of_node failed...\n");
return PTR_ERR(desc3);
}
return 0;
}
static void __exit mycdev_exit(void)
{
int i;
//灭灯
//gpio_set_value(gpiono, 0);
gpiod_set_value(desc1, 0);
gpiod_set_value(desc2, 0);
gpiod_set_value(desc3, 0);
//释放申请的GPIO编号
//gpio_free(gpiono);
gpiod_put(desc1);
gpiod_put(desc2);
gpiod_put(desc3);
//注销中断
for(i=0;i<MAX;i++)
{
free_irq(irqno[i], (void *)i);
}
unregister_chrdev(major, "myled");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");