imx6ull _linux驱动成长笔记

imx6ull _linux驱动成长笔记

1.挂载问题

1.1开机自动挂载

修改/etc/fstab文件
cat /etc/fstab     //文件内容
vim /etc/fstab     //文件内容


查看nfs相关进程
ps -A | grep nfs    


检查是否有其他进程正在使用 /mnt 目录下的文件或资源
lsof | grep /mnt


遇到的问题,时常在/mnt目录里面查看不到nfs信息

有可能是重复挂载,直接umount /mnt 重新挂载
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/psd/nfs_rootfs /mnt

2.驱动程序基础操作

手动安装驱动程序

insmod demo.ko
卸载驱动
rmmod demo

查看驱动 
cat /proc/devices        //没创建设备节点的驱动程序这里面不会存在
字符设备节点创建成功,在/dev/目录下也会产生对应设备

打印内核log

dmesg (从内核启动到当前时候内核打印的所有信息)
sudo dmesg -C //直接清除打印信息
sudo dmesg -c //先将信息显示到终端上然后在清除

查看现象信息

cat /proc/devices
打印设备是否正确安装

编译

arm-buildroot-linux-gnueabihf-gcc usr_led_ctrl.c 
注意:编译过程中,modname必须是文件名,不然会编译失败
错误码:
-ENOMEM:内存不足,无法完成字符设备的注册。
-ENODEV:未找到对应的设备节点或设备文件。
-ENXIO:无效的设备或设备不存在。
-EEXIST:字符设备已经存在,不能重复注册。
-EBUSY:字符设备被占用,无法注册。
-EINVAL:无效的参数或配置选项。
-ENFILE:系统打开的文件数已达到上限。
-ENOMEM:内存不足。
-ENAMETOOLONG:设备名称过长。

3. led控制部分

3.1 ioremap方式

ioremap/iounmap函数的API
#include <linux/io.h>
void  *ioremap(phys_addr_t offset, unsigned long size)
功能:地址映射工作
参数:
    @offset:物理地址
    @size:映射的大小(单位是字节)
返回值:成功返回虚拟地址,失败返回NULL

void iounmap(void  *addr)
功能:取消地址映射
参数:
    @addr:虚拟地址
返回值:无

数据传输的API

#include <linux/uaccess.h>

int copy_from_user(void *to, const void __user volatile *from,
     unsigned long n)
功能:将数据从用户空间拷贝到内核空间 (驱动的write)
参数:
    @to:内核空间地址
 @from:用户空间的地址
 @n:拷贝的大小(单位字节)
返回值:成功返回0,失败返回未拷贝的字节的个数
        
int copy_to_user(void __user volatile *to, const void *from,
          unsigned long n)
功能:将数据从内核空间拷贝到用户空间 (驱动的read函数中)
参数:
    @to:用户空间地址
 @from:内核空间的地址
 @n:拷贝的大小(单位字节)
返回值:成功返回0,失败返回未拷贝的字节的个数

字符设备驱动的API

#include <linux/fs.h>
int register_chrdev(unsigned int major, const char *name,
                    const struct file_operations *fops)
功能:注册字符设备驱动
参数:
    @major:主设备号 次设备号[0-255]
        major > 0 :静态指定设备号 主:0-511
        major = 0 :动态分配主设备号
    @name:驱动的名字
        cat /proc/devices 
          1  mem
          4  /dev/vc/0
    |        |
        主设备号 名字
 @fops:操作方法结构体
        struct file_operations {
            int (*open) (struct inode *, struct file *);
            ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
            ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
            int (*release) (struct inode *, struct file *);
        };
返回值:
       major > 0 成功返回0,失败返回错误码
       major = 0 成功返回主设备号,失败返回错误码

void unregister_chrdev(unsigned int major, const char *name)
功能:注销字符设备驱动
参数:
    @major:主设备号
 @name:名字
返回值:无

用户空间的API

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:打开文件
参数:
    @pathname:打开文件的路径及名字
 @flags   :打开文件的方式
        O_RDONLY :只读
        O_WRONLY :只写
        O_RDWR   :读写
  O_APPEND :追加
  O_CREAT  :创建,如果flags中填写了O_CREAT,就必须填写第三个参数,
             第三个参数代表创建文件的权限
  O_TRUNC  :清空
  O_EXCL   :和O_CREAT结合使用,在创建文件的时候加上这个选项,如果文件存
           在就返回EEXIST,如果文件不存在就会不返回错误了
 @mode:创建文件的权限
        实际创建出来的文件的权限 = (mode & ~umask)
        umask:文件的掩码:通过umask命令可以查看掩码的默认值是0002  
        ~umask:文件掩码的取反是在最大文件的权限的基础上取反的,最大文件的权限是0666
  实际创建出来的文件的权限 = (0666 & ~(0002)) =  (0666 & 0664) = 0664
返回值:成功返回文件描述符,失败返回-1置位错误码

            
ssize_t read(int fd, void *buf, size_t count);
功能:读取文件中的内容
参数:
    @fd:文件描述符
 @buf:存储读取到数据的首地址
 @count:想要读取的字节的个数
返回值:成功返回读取到字节的个数,如果返回0代表读取到文件的结尾了。
        失败返回-1置位错误码

ssize_t write(int fd, const void *buf, size_t count);
功能:向文件中写数据
参数:
    @fd:文件描述符
    @buf:想要写的数据的首地址
 @count:想要写的字节的个数
返回值:成功返回写入的字节的个数,0表示不会写任何的数据
     失败返回-1置位错误码

#include <unistd.h>

int close(int fd);
功能:关闭文件
参数:
    @fd:文件描述符
返回值:成功返回0,失败返回-1置位错误码

led_ctrl.h

#ifndef __LED_CTRL_DRV_H__
#define __LED_CTRL_DRV_H__

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <asm/io.h>

// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;
//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;

/* registers */
#define IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3_REG  0x02290000 + 0x14 
#define GPIO5_GDIR_REG 0x020AC004
#define GPIO5_DR_REG 0x020AC000

static int led_init(void)
{
    	/* ioremap */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3_REG, 4);
	
	// GPIO5_GDIR 地址:0x020AC004
	GPIO5_GDIR = ioremap(GPIO5_GDIR_REG, 4);
	
	//GPIO5_DR 地址:0x020AC000
	GPIO5_DR  = ioremap(GPIO5_DR_REG, 4);

    if(!IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 || !GPIO5_DR || !GPIO5_GDIR)
	{
		printk("ioremap error \n");
		return -1;
	}

	/* enable gpio5
	 * configure gpio5_io3 as gpio
	 * configure gpio5_io3 as output 
	 */
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;

	*GPIO5_GDIR |= (1<<3);

	return 0;
}
static int led_exit(void)
{
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	return 0;
}
static int led_on(void)
{
	/* set gpio to let led on */
	*GPIO5_DR &= ~(1<<3);
	return 0;
}
static int led_off(void)
{
	/* set gpio to let led off */
	*GPIO5_DR |= (1<<3);
	return 0;
}

#endif

led_ctrl_drv.c

#include "led_ctrl_drv.h"

#define HELLO_DRV_NAME "led_ctrl_drv"
#define MIN(a,b) (a < b ? a : b)
#define SIZE_KERNEL_BUF 1024
static int MAJOR;
static struct class *chr_cls;
struct device * dev;
static char kernel_buf[SIZE_KERNEL_BUF];

static int chrdev_open(struct inode *node, struct file *file)
{
    printk("led_ctrl_drv open:%s:%s:%d\n",__FILE__,__func__,__LINE__);
    if(led_init())
    {
        printk("%s:%s:%d:led_exit error \n",__FILE__,__func__,__LINE__);
        return -ENAVAIL;
    }
    return 0;
}
static ssize_t chrdev_read(struct file *file, char __user *usr_buf, size_t size_usr, loff_t *off)
{
    
    if(copy_to_user(usr_buf,kernel_buf,MIN(SIZE_KERNEL_BUF,size_usr)))
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    printk("chrdev_read:%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return MIN(SIZE_KERNEL_BUF,size_usr);
}

static ssize_t chrdev_write(struct file *file, const char __user *usr, size_t size_usr, loff_t *off)
{

    char val;
    if(copy_from_user(&val, usr, MIN(sizeof(val),size_usr)))
    {
        printk("copy_from_user err\n");
        return -EIO;
    }else if(val)
    {
        if(!led_on())
        {
            printk("%s:%s:%d: led_on error\n",__FILE__,__func__,__LINE__);
            return -EIO;
        }
    }else
    {
        if(!led_off())
        {
            printk("%s:%s:%d: led_off error\n",__FILE__,__func__,__LINE__);
            return -EIO;
        }
    }
    printk("chrdev_write:%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return MIN(SIZE_KERNEL_BUF,size_usr);
}

static int chrdev_close(struct inode * node, struct file * file)
{
    if(led_exit()){
        printk("%s:%s:%d:led_exit error \n",__FILE__,__func__,__LINE__);
        return -ENAVAIL;
    }
    printk("chrdev_close:%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

static const struct file_operations led_ctrl_fops = {
    .open = chrdev_open,
	.read = chrdev_read,
    .write = chrdev_write,
    .release = chrdev_close,
};

static int __init hello_drv_init(void)
{
    MAJOR = register_chrdev(0, HELLO_DRV_NAME,&led_ctrl_fops);
    if(MAJOR < 0)
    {
        printk("register_chrdev error %s:%s:%d\n",__FILE__,__func__,__LINE__);
        return MAJOR;
    }
    chr_cls = class_create(THIS_MODULE,HELLO_DRV_NAME);
    if(IS_ERR(chr_cls))
    {
        printk("class create error\n");
        return PTR_ERR(chr_cls);
    }
    dev = device_create(chr_cls, NULL,MKDEV(MAJOR, 0), NULL, HELLO_DRV_NAME);
    if(IS_ERR(dev))
    {
        printk("device create error\n");
        return PTR_ERR(dev);
    }

    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
static void __exit hello_drv_exit(void)
{
    led_exit();
    device_destroy(chr_cls, MKDEV(MAJOR, 0));
    class_destroy(chr_cls);
    unregister_chrdev(MAJOR, HELLO_DRV_NAME);
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}

module_init(hello_drv_init);
module_exit(hello_drv_exit);

MODULE_DESCRIPTION("led_ctrl driver");
MODULE_AUTHOR("psd 1614811057@qq.com");
MODULE_LICENSE("GPL");

usr_led_ctrl.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,const char **argv)
{
    int fd;
    char status = 0;
    if(argc != 3)
    {
        printf("Usage: %s <dev> <on|off>\n", argv[0]);
		printf("  eg: %s /dev/myled on\n", argv[0]);
		printf("  eg: %s /dev/myled off\n", argv[0]);
		return -1;
    }
    fd = open(argv[1],O_RDWR);
    if (fd < 0)
	{
		printf("can not open %s\n", argv[0]);
		return -1;
	}
    if(!strcmp("on",argv[2]))
    {
        status = 1;
    }
    write(fd,&status,sizeof(status));
    close(fd);
    return 0;
}

3.2设备树方式

设备树的处理过程是: dtb -> device_node -> platform_device。
3.2.1设备树基础操作

路径:

设备树存放路径:
arch/arm/boot/dts
arch/arm/boot/dts/100ask_imx6ull-14x14.dts
内核文档 Documentation/devicetree/bindings/

操作:

系统运行后设备树解析信息存放路径
cd /sys/firmware/devicetree/base
/proc/device-tree/
字符串信息 使用cat
数字信息   使用hexdump 

基础知识:

设备树属性就是键值对,键值对的组成格式如下:

文本字符串(以 null 结尾)用双引号表示:
	string-property = "a string"; #字符串
'Cells' 是由尖括号分隔的 32 位无符号整数:
	cell-property = <0xbeef 123 0xabcd1234>; #无符号32bit数
二进制数据用方括号分隔:
	binary-property = [01 23 45 67]; #16进程表示的单字节的数
不同表示的数据可以使用逗号连接在一起:
	mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
逗号也用于创建字符串列表:
	string-list = "red fish", "blue fish";


1.设备树每个节点对应的都是device_node结构体
    struct device_node {
        const char *name; //节点名 "mynode"
        const char *full_name; //节点全名 "mynode@0x12345678"
        struct device_node *parent; //父节点
        struct device_node *child;  //子节点
        struct device_node *sibling; //兄弟节点
        struct property *properties; 
        //节点内的所有的属性通过property构成单链表,每一个键值对都对应一个property
    };
2.属性结构体
    struct property {
        char *name; //键
        int length;    //值的长度 ,单位字节
        void *value; //值的首地址
        struct property *next; //指向下一个键值对
    };

3.节点获取的函数
    struct device_node *of_find_node_by_path(const char *path)
    功能:通过路径获取节点
 参数:
        @path:路径 "/mynode@0x12345678"
 返回值:成功返回节点首地址,失败返回NULL
 struct device_node *of_find_node_by_name(struct device_node *from,
                                             const char *name)
 功能:通过名字获取节点
 参数:
        @from:NULL,从根节点开始解析
        @name:节点名的字符串
 返回值:成功返回节点首地址,失败返回NULL
3.2.2设备树节点特性
  1. 节点取别名

    demo:demo@0x80000000{  //给demo@0x80000000取了别名,名字是demo
    
    };
    
    aliases{
     serial0=&uart4;
    };
    
    3.2.3节点引用
    demo:demo@0x80000000{  //给demo@0x80000000取了别名,名字是demo
     property1=value1;
    };
    
    &demo{ //引用上述节点,会将上述节点内的属性继承过来,
           //将property2=value2;添加进demo节点
     property2=value2;
    };
    
  2. 节点合并(同名节点合并)

3.2.3几个特殊属性
  1. compatible属性

    compatible = “厂商名,设备名”;

    ethernet{
     compatible = "3com,509";
    };
    含有 compatile 属性的子节点会被解析转换成paltform_device结构体
    
  2. reg属性

    可寻址设备使用以下属性将地址信息编码到设备树中:

    • reg
    • #address-cells
    • #size-cells

    每个可寻址设备都会得到一个reg元组列表,格式为reg = <address1 length1 [address2 length2] [address3 length3] ... >。每个元组代表设备使用的地址范围。每个地址值都是一个或多个称为单元格的 32 位整数的列表。同样,长度值可以是单元格列表,也可以是空的。

    由于地址和长度字段都是可变大小的,因此父节点中的#address-cells#size-cells属性用于说明每个字段中有多少个单元格。或者换句话说,正确解释 reg 属性需要父节点的#address-cells 和#size-cells 值。要查看这一切是如何工作的,让我们将寻址属性添加到示例设备树中,从 CPU 开始。

    / {
        #address-cells = <1>;
        #size-cells = <1>;
    
        serial@101f0000 {
            compatible = "arm,pl011";
            reg = <0x101f0000 0x1000>;
        };
        
        i2c1{
         #address-cells = <1>;
         #size-cells = <0>;
            
         si7006@40{
             reg = <0x40>;
            };
        }
    };
    
  3. 节点内属性删除

    /delete-property/;
    
3.2.4节点获取函数
struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:
 @path:路径 "/mynode@0x12345678"
返回值:成功返回节点首地址,失败返回NULL

struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
功能:通过名字获取节点
参数:
 @from:NULL,从根节点开始解析
 @name:节点名的字符串
返回值:成功返回节点首地址,失败返回NULL

struct device_node *of_find_compatible_node(
      struct device_node *from,
      const char *type,
      const char *compat)
功能:通过compatible获取节点
参数:
 @from:NULL,从根节点开始解析
    @type:NULL
 @compat:设备树中compatible填写的成员
返回值:成功返回节点首地址,失败返回NULL
3.2.5属性获取函数
struct property *of_find_property(const struct device_node *np,
      const char *name,
      int *lenp)
功能:获取节点内的属性
参数:
    @np:节点的指针
 @name:属性名
 @lenp:值的长度
返回值:成功返回property的结构体指针,失败返回NULL

    
static inline int of_property_read_u8_array(const struct device_node *np,
   const char *propname, u8 *out_values, size_t sz)
功能: 得到属性值中指定标号的8位数组的值
参数:
 @np        - 设备节点指针
 @propname  - 属性名称
 @out_values- 输出数据的首地址
 @sz        - 成员个数
返回值: 成功返回0,出错错误码   
    
int of_property_read_u32_array(const struct device_node *np,      
                         const char *propname,
                         u32 *out_values, size_t sz)
功能: 得到属性值中指定标号的32位数组的值
参数:
 @np        - 设备节点指针
 @propname  - 属性名称
 @out_values- 输出数据的首地址
 @sz        - 成员个数
返回值: 成功返回0,出错错误码
    
int of_property_read_string(struct device_node *np, const char *propname,
   const char **out_string)
功能:已知字符串属性健值对的名称,得到字符串的值
参数:
      np         -设备节点的地址
      propname   -属性健值对的名称
   out_string -填充字符串的值
返回值:成功0,出错错误码
3.2.6设备树操作led代码部分

100ask:

#define GROUP_PIN(g,p) ((g<<16) | (p))

/ {
	100ask_led@0 {
		compatible = "100as,leddrv";
		pin = <GROUP_PIN(3, 1)>;
	};

	100ask_led@1 {
		compatible = "100as,leddrv";
		pin = <GROUP_PIN(5, 8)>;
	};

};

cpu灯:

    leds {
        compatible = "gpio-leds";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_leds>;
        
        status = "disabled";

        led0: cpu {
            label = "cpu";
            gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
            default-state = "on";
            linux,default-trigger = "heartbeat";
        };
    };

dts:

psd_leds{
    compatible = "psd,leds";
    cpu_led {
        compatible = "psd,cpu_led";
        led1 = <&gpio5 3 0>;
        //default-state = "off";
        status = "disable";
    };
    gpio_leds{
        compatible = "psd,gpio_leds";
        led1 = <&gpio3 1 0>;
        led2 = <&gpio5 8 0>;
        status = "disable";
    };
};

命令:

cd /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88

make dtbs

cp /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dtb /home/psd/nfs_rootfs

过程:

自动创建设备节点的API
#include <linux/device.h>
/sys/class路径下创建目录name
    
struct class * class_create(owner, name)
功能:向上提交目录名
参数:
    @owner:都填写为THIS_MODULE
    @name:目录名
返回值:成功返回class结构体指针,失败返回错误码指针

/sys/class/name目录下创建文件

struct device *device_create(struct class *class, 
                 struct device *parent,dev_t devt, 
                 void *drvdata, const char *fmt, ...)
功能:向上提交创建节点的信息
参数:
    @class:cls的结构体指针
 @parent:一般填写为NULL
    @devt:设备号 
        MKDEV(major,minor)  //根据主次设备号合成设备号
        MAJOR(devno)        //根据设备号获取主设备号
     MINOR(devno)        //根据设备号获取次设备号
 @drvdata:一般填写为NULL
 @fmt,...:设备节点的名字 "mouse%d",i
返回值:成功返回结构体指针,失败返回错误码指针

void class_destroy(struct class *cls)
功能:销毁class
参数:
    @cls:class结构体指针
返回值:无
void device_destroy(struct class *class, dev_t devt)
功能:销毁device
参数:
    @cls:class结构体指针
    @devt:设备号
返回值:无



最终,文件系统会将上面创建的信息重新在/dev目录下再创建一个类似的目录和文件
        
ISERR()判断是否是错误码 是则为真
PTR_ERR()返回错误码
        
        
if(IS_ERR(cls)){  //如果是错误IS_ERR返回真,否则返回假
    printk("class create error\n");
    return  PTR_ERR(cls);  //返回错误码
}
1.init();
注册字符设备register_chrdev(主设备号,设备名,文件操作结构体指针)
创建设备和驱动程序关联类class_create()
 #include <sys/ioctl.h>
 int ioctl(int fd, unsigned long request, ...);
 功能:设备的控制
 参数:
        @fd:文件描述符
        @request:请求码
  @...:可变参数,可写(地址),可不写
 返回值:成功返回0,失败返回-1置位错误码

3.3 gpio子系统的API

struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:
    @path:路径  "/mynode@0x12345678"
返回值:成功返回节点的指针,失败返回NULL

int of_get_named_gpio(struct device_node *np,
                        const char *propname, int index)
功能:获取gpio的编号
参数:
    @np:节点的指针
    @propname:键名
    @index:编号
返回值:成功返回gpio的编号,失败返回错误码
int gpio_request(unsigned gpio, const char *label)
功能:申请要使用的gpio
参数:
    @gpio:gpio编号(设备树)
    @label:名字(NULL)
返回值:成功返回0,失败返回错误码
        
int gpio_direction_input(unsigned gpio)
功能:设置管脚的方向为输入
参数:
    @gpio:gpio编号
返回值:成功返回0,失败返回错误码
        
int gpio_direction_output(unsigned gpio, int value) 
功能:设置管脚的方向为输出
参数:
    @gpio:gpio编号
    @value:10低电平
返回值:成功返回0,失败返回错误码
        
void gpio_set_value(unsigned gpio, int value)
功能:设置电平的状态   
参数:
    @gpio:gpio编号
    @value:10低电平
返回值:无
           
int gpio_get_value(unsigned gpio)
功能:获取电平的状态
参数:
    @gpio:gpio编号
返回值:1高电平,0低电平

void gpio_free(unsigned gpio)
功能:释放gpio
参数:
    @gpio:gpio编号
返回值:无

成品:

kmd:

.h

#ifndef __LED_CTRL_H__
#define __LED_CTRL_H__

#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define CNAME "led_gpio_ctrl_drv"
#define LED_NUM 2

enum leds{
    LED1,
    LED2,
    LED3
};

enum leds_status{
    LED_ON,
    LED_OFF
};

#define ON _IOW('a',1,int)
#define OFF _IOW('a',0,int)
#define GET_CMD_SIZE(cmd) ((cmd>>16) & 0x3fff)
#define MIN(a,b) (a < b ? a : b)

typedef struct led_ctrl {
    char dev_name[20];
    int major;
    struct class * led_dir;
    struct device * led_file;
    struct device_node * led_device_node[20];
    int gpiono[20];
    int led_num;
    // int (* led_ctrl_deinit) (void);
} led_ctrl_t;

static void KMD_ERR(const char *msg);
static int led_ctrl_init(void);
static int led_ctrl_deinit(void);
static int get_dt_data(void);
static int LedCtrl(const int which, const int status);
#endif

.c

#include"led_ctrl.h"
#include <linux/init.h>
#include <linux/module.h>


    // psd_leds {
    //     compatible = "psd,psd_leds";
    //     cpu_led {
    //         compatible = "psd,cpu_led";
    //         led1 = <&gpio5 3 0>;
    //         //default-state = "off";
    //         status = "disable";
    //     };
    //     gpio_leds{
    //         compatible = "psd,gpio_leds";
    //         led1 = <&gpio3 1 0>;
    //         led2 = <&gpio5 8 0>;
    //         status = "disable";
    //     };
    // };

led_ctrl_t psd_led_ctrl = {
    .dev_name = CNAME,
    .led_num = LED_NUM,
};
static int get_dt_data(void)
{
    int ret;
    psd_led_ctrl.led_device_node[0] = of_find_node_by_path("/psd_leds/cpu_led");
    psd_led_ctrl.led_device_node[1] = of_find_node_by_path("/psd_leds/gpio_leds");
    if((psd_led_ctrl.led_device_node[0]  == NULL) || (psd_led_ctrl.led_device_node[1]  == NULL))
    {
        KMD_ERR("of_find_node_by_path error");
        return -ENODATA;
    }
    psd_led_ctrl.gpiono[0] = of_get_named_gpio(psd_led_ctrl.led_device_node[0],"led1",0);
    psd_led_ctrl.gpiono[1] = of_get_named_gpio(psd_led_ctrl.led_device_node[1],"led1",0);
    psd_led_ctrl.gpiono[2] = of_get_named_gpio(psd_led_ctrl.led_device_node[1],"led2",0);
    if((psd_led_ctrl.gpiono[0] < 0) || (psd_led_ctrl.gpiono[1] < 0) || (psd_led_ctrl.gpiono[2] < 0))
    {
        KMD_ERR("of_get_named_gpio error");
        return -ENODATA;
    }
    if ((ret = gpio_request(psd_led_ctrl.gpiono[0], NULL)) < 0) {
        KMD_ERR("gpio_request psd_led_ctrl.gpiono[0] error");
        return ret;
    }
    if ((ret = gpio_request(psd_led_ctrl.gpiono[1], NULL)) < 0) {
        KMD_ERR("gpio_request psd_led_ctrl.gpiono[1] error");
        return ret;
    }
    // if ((ret = gpio_request(psd_led_ctrl.gpiono[2], NULL)) < 0) {
    //     KMD_ERR("gpio_request psd_led_ctrl.gpiono[2] error");
    //     return ret;
    // }
    if (((ret = gpio_direction_output(psd_led_ctrl.gpiono[0], 1)) < 0) || 
        ((ret = gpio_direction_output(psd_led_ctrl.gpiono[1], 1)) < 0) ) {
        KMD_ERR("gpio_direction_output error");
        return ret;
    }
    return 0;
}

static int LedCtrl(const int which,const int status)
{
    int led_num = which;
    switch(led_num)
    {
        case LED1:
            gpio_set_value(psd_led_ctrl.gpiono[0], status);
            break;
        case LED2:
            gpio_set_value(psd_led_ctrl.gpiono[1], status);
            break;
        // case LED3:
        //     gpio_set_value(psd_led_ctrl.gpiono[2], status);
            break;
    }
    return 0;
}

int psd_led_open(struct inode * node, struct file * file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

    return 0;
}

long psd_led_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
    int ret;
    int which;
    if(copy_from_user(&which, (void *)arg, GET_CMD_SIZE(arg)))
    {
        KMD_ERR("copy_from_user ERROR");
        ret = -EFAULT;
    }
    switch(cmd)
    {
        case ON:
            LedCtrl(which, LED_ON);
        break;
        case OFF:
            LedCtrl(which, LED_OFF);
        break;
        default: // 处理未知命令
            ret = -EINVAL;
            break;
    }
    return ret;
}

int psd_led_release(struct inode * node, struct file * file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

static const struct file_operations psd_led_file_operations = {
    .open		    = psd_led_open,
    .unlocked_ioctl = psd_led_ioctl,
	.release	    = psd_led_release,
};

static int led_ctrl_init(void)
{
    
    //register chrdev
    psd_led_ctrl.major = register_chrdev(0,psd_led_ctrl.dev_name,&psd_led_file_operations);
    if(psd_led_ctrl.major < 0)
    {
        KMD_ERR("register_chrdev error");
        return -EBUSY;
    }
    psd_led_ctrl.led_dir = class_create(THIS_MODULE, psd_led_ctrl.dev_name);
    if(IS_ERR(psd_led_ctrl.led_dir))
    {
        KMD_ERR("class_create Error");
        goto ERR1;
    }
    psd_led_ctrl.led_file = device_create(psd_led_ctrl.led_dir, NULL, 
                            MKDEV(psd_led_ctrl.major, 0), NULL,psd_led_ctrl.dev_name);
    if(IS_ERR(psd_led_ctrl.led_file))
    {
        KMD_ERR("device_create Error");
        goto ERR2;
    }
    return 0;
ERR2:
    class_destroy(psd_led_ctrl.led_dir);
ERR1:
    unregister_chrdev(psd_led_ctrl.major, psd_led_ctrl.dev_name);
    return  PTR_ERR(psd_led_ctrl.led_dir);
}

static int led_ctrl_deinit(void)
{
    int i;
    device_destroy(psd_led_ctrl.led_dir, MKDEV(psd_led_ctrl.major, 0));
    class_destroy(psd_led_ctrl.led_dir);
    unregister_chrdev(psd_led_ctrl.major, psd_led_ctrl.dev_name);
    for(i = 0; i < LED_NUM; i++)
    {
        gpio_set_value(psd_led_ctrl.gpiono[i], 0);
        gpio_free(psd_led_ctrl.gpiono[i]);
    }
    return 0;
}
static void KMD_ERR(const char *msg)
{
    printk(KERN_ERR "Error: %s\n", msg);
}

static int __init led_gpio_ctrl_init(void)
{
    int ret;
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    ret = led_ctrl_init();
    if(ret)
    {
        return ret;
    }
    ret = get_dt_data();
    if(ret)
    {
        return ret;
    }
    // gpio_set_value(psd_led_ctrl.gpiono[1], 0);
    // gpio_set_value(psd_led_ctrl.gpiono[0], 1);
    return 0;
}

static void __exit led_gpio_ctrl_exit(void)
{
    led_ctrl_deinit();
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}

module_init(led_gpio_ctrl_init);
module_exit(led_gpio_ctrl_exit);
MODULE_LICENSE("GPL");

usr:

.h

#ifndef __LED_H__
#define __LED_H__

#define LED_NUM 2

enum leds{
    LED1,
    LED2,
    LED3
};

#define ON _IOW('a',1,int)
#define OFF _IOW('a',0,int)

enum leds_status{
    LED_ON,
    LED_OFF
};

#endif

.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include "led.h"

#define LED_DEVICE "/dev/led_gpio_ctrl_drv"
int main(int argc,const char **argv)
{
    int fd;
    char status = 0;
    if(argc != 3)
    {
		printf("  eg: ./a.out ledx on/off\n");
		return -1;
    }
    fd = open(LED_DEVICE,O_RDWR);
    if (fd < 0)
	{
		printf("can not open %s\n", argv[0]);
		return -1;
	}
    if((!strcmp("led1",argv[1])) || (!strcmp("on",argv[2])) )
    {
        if(ioctl(fd, ON, LED1))
        {
            printf("IOCTL ERROR \n");
            return -1;
        }
    }else if((!strcmp("led1",argv[1])) || (!strcmp("off",argv[2])))
    {
        if(ioctl(fd, OFF, LED1))
        {
            printf("IOCTL ERROR \n");
            return -1;
        }
    }
    if((!strcmp("led2",argv[1])) || (!strcmp("on",argv[2])) )
    {
        if(ioctl(fd, ON, LED2))
        {
            printf("IOCTL ERROR \n");
            return -1;
        }
    }else if((!strcmp("led2",argv[1])) || (!strcmp("off",argv[2])))
    {
        if(ioctl(fd, OFF, LED2))
        {
            printf("IOCTL ERROR \n");
            return -1;
        }
    }
    close(fd);
    return 0;
}
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值