1、添加设备树节点
![](https://i-blog.csdnimg.cn/blog_migrate/20cb6901c3827fc41335fd24b7f02704.png)
2、驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <asm/io.h>
#define LED_CNT 1 /*设备个数*/
#define LED_NAME "irq" /*设备名字*/
int err;
int temp;
struct led_dev{
int major;/*主设备号*/
int minor;/*次设备号*/
dev_t devid; /*设备号*/
struct cdev cdev; /*字符设备*/
struct class *class;/*类 */
struct device *device; /*设备*/
struct device_node *dnode;/*设备树的节点信息*/
int gpioled1,gpioled2,gpio3;/*GPIO号*/
int irqnum; //中断号
irqreturn_t (*handler)(int,void*); //中断处理函数
};
struct led_dev ledev;
//中断处理函数
static irqreturn_t led_handler(int irq, void *dev_id){
int val = 0;
struct led_dev *dev=(struct led_dev*)dev_id;
val=gpio_get_value(dev->gpio3);
if(val == 0){
gpio_set_value(ledev.gpioled1,1);
gpio_set_value(ledev.gpioled2,0);
}else if(val == 1){
gpio_set_value(ledev.gpioled1, 0);
gpio_set_value(ledev.gpioled2, 1);
}
return IRQ_HANDLED;
}
/*led_fops的 led_open*/
static int led_open(struct inode *inode, struct file *file)
{
return 0;
}
/*led_fops的led_read*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
return 0;
}
/*led_fops的led_write*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
return 0;
}
/*led_fops的led_release*/
static int led_release(struct inode *inode, struct file *file)
{
return 0;
}
/*外部控制*/
static const struct file_operations led_fops={
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.open = led_open,
.release = led_release,
};
static const struct of_device_id gpio_led_match[] = {
{ .compatible = "hello_test", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_led_match);
static int gpio_led_probe(struct platform_device * pdev)
{
int ret = 0;
printk("hello test probe");
/*注册字符设备*/
/*1、申请设备号*/
ledev.major=0;
if(ledev.major){/*定义设备号*/
ledev.devid=MKDEV(ledev.major,0);
ret=register_chrdev_region(ledev.devid,LED_CNT,LED_NAME);
}else{/*没有给定设备号*/
ret = alloc_chrdev_region(&ledev.devid,0,LED_CNT,LED_NAME);
ledev.major = MAJOR(ledev.devid);
ledev.minor = MINOR(ledev.devid);
}
if(ret < 0){
return ENXIO;
}
/*2、添加字符设备*/
ledev.cdev.owner = THIS_MODULE;
cdev_init(&ledev.cdev,&led_fops);//申请空间和简单的初始化,led_fops函数是指向处理与设备实际通信的函数
ret = cdev_add(&ledev.cdev,ledev.devid,LED_CNT);
if(ret < 0){
return ENOTBLK;
}
ledev.class = class_create(THIS_MODULE,LED_NAME);//class_create函数动态的创建设备的逻辑类,完成部分字段的初始化,
//然后添加到Linux的内核中去,效果是创建一个以第二个参数为文件的空文件名
if(IS_ERR(ledev.class)){
ret=PTR_ERR(ledev.device);
return ENODEV;
}
ledev.device = device_create(ledev.class,NULL,ledev.devid,NULL,LED_NAME);
//device_create能自动创建设备文件是依赖于udev这个应用程序,设备文件通常放在/dev目录下。
//使用udev后,在/dev目录下就只包含系统中真正存在的设备。
if(IS_ERR(ledev.device)){
ret = PTR_ERR(ledev.device);
return ENODEV;
}
/*解析设备树节点信息,通过名字*/
ledev.dnode = pdev->dev.of_node;
/*根据设备结点的信息结构体获取gpio编号*/
ledev.gpioled1 = of_get_named_gpio(ledev.dnode,"red-gpios",0);
if(ledev.gpioled1<0){
printk("of_get_named_gpio failed1!\n");
return ENOTDIR;
}
printk("gpioled1 = %d \n",ledev.gpioled1);
ledev.gpioled2 = of_get_named_gpio(ledev.dnode,"green-gpios",0);
if(ledev.gpioled2<0){
printk("of_get_named_gpio failed2!\n");
return ENOTDIR;
}
printk("gpioled2 = %d \n",ledev.gpioled2);
ledev.gpio3 = of_get_named_gpio(ledev.dnode,"irq-gpios",0);
if(ledev.gpio3 < 0){
printk("of_get_named_gpio failed3!\n");
return ENOTDIR;
}
/*申请GPIO号*/
err = gpio_request(ledev.gpioled1,"gpioled1");
if(err < 0 )
{
printk("gpio_request1!\n");
return ENOTBLK;
}
err = gpio_request(ledev.gpioled2,"gpioled2");
if(err < 0 )
{
printk("gpio_request2!\n");
return ENOTBLK;
}
err = gpio_request(ledev.gpio3,"gpio");
if(err < 0 )
{
printk("gpio_request3!\n");
return ENOTBLK;
}
/*设置GPIO方向为输出并默认输出低电平*/
err = gpio_direction_output(ledev.gpioled1,0);
if(err<0){
printk("gpio_direction_output!\n");
return EFAULT;
}
err = gpio_direction_output(ledev.gpioled2,0);
if(err<0){
printk("gpio_direction_output!\n");
return EFAULT;
}
/*设置GPIO方向为输入*/
gpio_direction_input(ledev.gpio3);
//申请中断号码
ledev.irqnum = gpio_to_irq(ledev.gpio3);
ledev.handler = led_handler;
ret = request_irq(ledev.irqnum,ledev.handler,
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"gpio3",&ledev);
if(ret){
printk("irq %d request failed!\r\n",ledev.irqnum);
gpio_free(ledev.gpio3);
return -1;
}
/*默认灯打开状况*/
gpio_set_value(ledev.gpioled1,1);
gpio_set_value(ledev.gpioled2,0);
return 0;
}
static void gpio_led_shutdown()
{
printk("shutdown\n");
}
static int gpio_led_remove(struct platform_device *pdev){
gpio_set_value(ledev.gpioled1,0);
gpio_set_value(ledev.gpioled2,0);
gpio_free(ledev.gpioled1);
gpio_free(ledev.gpioled2);
/*删除设备的类*/
device_destroy(ledev.class ,ledev.devid);
/*删除类*/
class_destroy(ledev.class);
/*删除设备*/
cdev_del(&ledev.cdev);
/*注销设备号*/
unregister_chrdev_region(ledev.devid, LED_CNT);
//注销中断
free_irq(ledev.irqnum,&ledev);
//释放中断号
gpio_free(ledev.gpio3);
return 0;
}
static struct platform_driver gpio_led_driver={
.probe = gpio_led_probe,//探测方法和设备的初始化操作
.shutdown = gpio_led_shutdown,
.remove = gpio_led_remove,
.driver = {
.name = LED_NAME,//设备树节点名称
.of_match_table = gpio_led_match,//定义兼容的驱动属性
}
};
static int __init led_init(){
printk("led_init !\n");
return platform_driver_register(&gpio_led_driver);
}
static void __exit led_exit(){
printk("led_exit \n!");
platform_driver_unregister(&gpio_led_driver);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shakespeare");
3、应用程序
int main()
{
int fd,ret,retvalue;
unsigned int cmd;
unsigned int arg;
char str [100];
char *filename;
filename = "./dev/led";
fd = open(filename,O_RDWR);
printf("请输入cmd:");
ret=scanf("%d",&cmd);
if(ret !=1){
return -1;
}
if(cmd == 1){
ioctl(fd,RED_CMD,&arg);
}else if(cmd == 2){
ioctl(fd,GREEN_CMD,&arg);
}else if(cmd == 3){
while(1){
arg=0;
ret = ioctl(fd,RG_CMD,&arg);
if(ret <0)
{
return -1;
}
sleep(1);
arg=1;
ret = ioctl(fd,RG_CMD,&arg);
if(ret <0)
{
return -1;
}
sleep(1);
}
}
close(fd);
return 0;
}