个人技术分享

个人笔记

首先在 dts 设备数里面添加 led 节点信息。

编译后将 dtb 文件写入版子中。

然后就是写驱动

首先第一步是驱动的注册与卸载

static int dtsled_init(void)
{
    int ret = 0;
    return ret;
}

static void dtsled_exit(void)
{

}

//注册与卸载
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhju");

创建设备结构体:

struct dtsled_dev{
    dev_t devid;    //设备号
    struct cdev cdev; //字符设备
    struct class *class; //创建类
    struct device *device;//创建设备节点
    int major;      //主设备号
    int minor;
    struct device_node *nd; //设备节点信息

};

struct dtsled_dev dtsled;

创建好了之后就要注册设备号:

    //1.注册设备号
    dtsled.major = 0;
    if(dtsled.major){//给定了设备号
        dtsled.devid = MKDEV(dtsled.major,0);
        ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
    }else{//未给定 自己申请
        ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
    }
    if(ret < 0){
        goto fail_devid;
    }

fail_devid:
    return ret;

若是自己给定了设备号就通过 register_chrdev_region 来注册

若是没有给就通过 alloc_chrdev_region 让系统自动注册设备

第二步添加字符设备

static const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE,
    .write = dtsled_write,
    .open = dtsled_open,
    .release = dtsled_release,
};

dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev, &dtsled_fops);
ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
if(ret < 0){
   goto fail_cdev;
}

fail_cdev:
    unregister_chrdev_region(dtsled.devid, DTSLED_CNT);

通过 cdev_init 函数把 dtsled 结构体中的字符设备信息初始化,把能够操作的函数给到 dtsled 结构体,并通过 cdev_add 函数注册字符设备。若注册失败,就要释放掉之前注册成功过的设备号。

第三步创建节点

首先创建类

    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
    if(IS_ERR(dtsled.class)){
        ret = PTR_ERR(dtsled.class);
        goto fail_class;
    }

fail_class:
    cdev_del(&dtsled.cdev);

通过 class_create 函数来创建类,若是创建失败,就要注销创建的字符设备。

然后创建设设备

    dtsled.device = device_create(dtsled.class,NULL,dtsled.devid,NULL,DTSLED_NAME);
    if(IS_ERR(dtsled.device)){
        ret = PTR_ERR(dtsled.device);
        goto fail_device;
    }

fail_device:
    class_destroy(dtsled.class);

通过 device_create 函数来创建设备 若是失败了就摧毁创建的类。

接着就可以通过 of 函数来获取设备树中的设备信息。

 //获取设备属性内容
    dtsled.nd = of_find_node_by_path("/led");//从设备树路径获得设备信息
    if(dtsled.nd == NULL){//如未找到
        ret = -EINVAL;
        goto fail_finded;
    }
    //获取属性
    ret = of_property_read_string(dtsled.nd, "status",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("status = %s\r\n", str);
    }

    ret = of_property_read_string(dtsled.nd, "compatible", &str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("compatible = %s\r\n", str);
    }
#if 0
    //获取寄存器属性
    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("regdata: \r\n");
        for(i=0;i<10;i++){
            printk("%#X ", regdata[i]);
        }
           printk("\r\n");
    }

    //led灯初始化

    //内存映射
    IMX6U_CCM_CCGR1 = ioremap(regdata[0],regdata[1]);
    SW_MUX_GPIO1_IO03 = ioremap(regdata[2],regdata[3]);
    SW_PAD_GPIO1_IO03 = ioremap(regdata[4],regdata[5]);
    GPIO1_DR = ioremap(regdata[6],regdata[7]);
    GPIO1_GDIR = ioremap(regdata[8],regdata[9]);
#endif
    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd,0);
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd,1);
    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd,2);
    GPIO1_DR = of_iomap(dtsled.nd,3);
    GPIO1_GDIR = of_iomap(dtsled.nd,4);
    
   //时钟初始化
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26);
    val |= 3 << 26;
    writel(val,IMX6U_CCM_CCGR1);

    writel(0x5,SW_MUX_GPIO1_IO03);
    writel(0X10B0,SW_PAD_GPIO1_IO03);

    val = readl(GPIO1_GDIR);
    val |= 1 << 3;
    writel(val,GPIO1_GDIR);

    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val,GPIO1_DR);

    return ret;

fail_rs:

of_iomap 函数可以获取设备的物理地址,并且映射为虚拟地址返回。

完整的 dtsled.c 文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#define DTSLED_CNT      1    //设备号个数
#define DTSLED_NAME     "dtsled" 

//地址映射后的指针
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

#define LEDOFF  0
#define LEDON   1

//dtsled设备结构体
struct dtsled_dev{
    dev_t devid;    //设备号
    struct cdev cdev; //字符设备
    struct class *class; //创建类
    struct device *device;//创建设备节点
    int major;      //主设备号
    int minor;
    struct device_node *nd; //设备节点信息

};

struct dtsled_dev dtsled;

static void led_switch(unsigned char dat)
{
    u32 val = 0;
    if(dat == LEDON){
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }else if(dat == LEDOFF){
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
}

static int dtsled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &dtsled;
    return 0;
}

static int dtsled_release(struct inode *inode, struct file *filp)
{
    struct  dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
    return 0;
}

static ssize_t dtsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int ret = 0;
    unsigned char databuf[1];
    struct dtsled_dev *dev = (struct dtssled_dev*)filp->private_data;

    ret = copy_from_user(databuf, buf, count);
    if(ret < 0){
        printk("kernel write error\r\n");
        return -EFAULT;
    }
    led_switch(databuf[0]);

    return 0;
}

static const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE,
    .write = dtsled_write,
    .open = dtsled_open,
    .release = dtsled_release,
};

static int dtsled_init(void)
{
    int ret = 0,val = 0;
    const char *str;
    u32 regdata[10];
    u8 i;

    //1.注册设备号
    dtsled.major = 0;
    if(dtsled.major){//给定了设备号
        dtsled.devid = MKDEV(dtsled.major,0);
        ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
    }else{//未给定 自己申请
        ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
    }
    if(ret < 0){
        goto fail_devid;
    }

    //2.添加字符设备
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev, &dtsled_fops);
    ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
    if(ret < 0){
        goto fail_cdev;
    }

    //3.自动创建节点

    //3.1创建类
    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
    if(IS_ERR(dtsled.class)){
        ret = PTR_ERR(dtsled.class);
        goto fail_class;
    }

    //设备
    dtsled.device = device_create(dtsled.class,NULL,dtsled.devid,NULL,DTSLED_NAME);
    if(IS_ERR(dtsled.device)){
        ret = PTR_ERR(dtsled.device);
        goto fail_device;
    }
    
    //获取设备属性内容
    dtsled.nd = of_find_node_by_path("/led");//从设备树路径获得设备信息
    if(dtsled.nd == NULL){//如未找到
        ret = -EINVAL;
        goto fail_finded;
    }
    //获取属性
    ret = of_property_read_string(dtsled.nd, "status",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("status = %s\r\n", str);
    }

    ret = of_property_read_string(dtsled.nd, "compatible", &str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("compatible = %s\r\n", str);
    }
#if 0
    //获取寄存器属性
    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("regdata: \r\n");
        for(i=0;i<10;i++){
            printk("%#X ", regdata[i]);
        }
           printk("\r\n");
    }

    //led灯初始化

    //内存映射
    IMX6U_CCM_CCGR1 = ioremap(regdata[0],regdata[1]);
    SW_MUX_GPIO1_IO03 = ioremap(regdata[2],regdata[3]);
    SW_PAD_GPIO1_IO03 = ioremap(regdata[4],regdata[5]);
    GPIO1_DR = ioremap(regdata[6],regdata[7]);
    GPIO1_GDIR = ioremap(regdata[8],regdata[9]);
#endif
    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd,0);
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd,1);
    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd,2);
    GPIO1_DR = of_iomap(dtsled.nd,3);
    GPIO1_GDIR = of_iomap(dtsled.nd,4);
    
   //时钟初始化
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26);
    val |= 3 << 26;
    writel(val,IMX6U_CCM_CCGR1);

    writel(0x5,SW_MUX_GPIO1_IO03);
    writel(0X10B0,SW_PAD_GPIO1_IO03);

    val = readl(GPIO1_GDIR);
    val |= 1 << 3;
    writel(val,GPIO1_GDIR);

    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val,GPIO1_DR);

    return ret;

fail_rs:
fail_finded:
    device_destroy(dtsled.class, dtsled.devid);
fail_device:
    class_destroy(dtsled.class);
fail_class:
    cdev_del(&dtsled.cdev);
fail_cdev:
    unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
fail_devid:
    return ret;
}

static void dtsled_exit(void)
{
    unsigned int val=0;
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);
    //释放内存映射
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
    //释放设备
    cdev_del(&dtsled.cdev);
    //释放设别号
    unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
    //摧毁设备
    device_destroy(dtsled.class, dtsled.devid);
    //摧毁类
    class_destroy(dtsled.class);
}
//注册与卸载
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhju");