中断顶半部:不允许耗时操作
代码流程:
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 3struct 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,那么实际长度为kbufsize = 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");