• 九、i2c设备驱动


    一、前言

    前面第二篇文章中,我总结了Linux系统下i2c驱动中的适配器驱动,但是一个完整的总线-设备驱动模型应该包含总线驱动和设备驱动,总线驱动也就是前面所总结的i2c适配器驱动,现在再来总结一下i2c设备驱动的具体实现步骤。

    二、硬件平台及内核版本

    硬件平台:NXP I.MX6Q(四核)

    Kernel版本:3.0.35(不支持设备树)

    三、代码实现

    1.在板级文件中添加i2c设备信息

    (1)首先找到板级文件,我的文件是:arch/arm/mach-mx6/board-mx6q_sabresd.c

    i2c的设备信息是在板级文件中通过i2c_board_info来描述的,因此找到i2c_board_info并添加我的i2c设备信息

    static struct i2c_board_info mxc_i2c1_board_info[] __initdata =
    {
        {
            I2C_BOARD_INFO("mxc_hdmi_i2c", 0x50),
        },
        {
            //我增加的一个虚拟的I2C设备
            I2C_BOARD_INFO("xxx_i2c", 0x30),//设备名字是xxx_i2c,地址是0x30
        },
    #ifdef CONFIG_SND_SOC_IMX_SGTL5000
        {
            I2C_BOARD_INFO("sgtl5000", 0x0a),
        },
    #endif
    #ifdef CONFIG_MXC_CAMERA_OV3640
        {
            I2C_BOARD_INFO("ov3640", 0x3c),//0x78 0x3c
            .platform_data = (void *) &camera_data_ov3640,
        },
    #endif
    };

    (2)在板级初始化函数中初始化设备

    找到mx6_sabresd_board_init()函数,这个函数里面调用了很多初始化函数,除了我们自己的i2c设备之外,它还调用很多外设的初始化函数,比如spi、uart等

    static void __init mx6_sabresd_board_init(void)
    {
        ...
        ...
        mx6q_sabresd_init_uart();//初始化串口
        ...
        ...
        imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data);
        imx6q_add_imx_i2c(1, &mx6q_sabresd_i2c_data);
        imx6q_add_imx_i2c(2, &mx6q_sabresd_i2c_data);
        i2c_register_board_info(0, mxc_i2c0_board_info,
                                ARRAY_SIZE(mxc_i2c0_board_info));
        //注册刚刚新增加的i2c设备
        i2c_register_board_info(1, mxc_i2c1_board_info,
                                ARRAY_SIZE(mxc_i2c1_board_info));
        i2c_register_board_info(2, mxc_i2c2_board_info,
                                ARRAY_SIZE(mxc_i2c2_board_info));  
        ...
        ...
    }

    通过以上两个步骤,新增加的i2c设备就被添加到系统当中了,此时设备的名字是"xxx_i2c",当我们在编写i2c设备驱动的时候,驱动名字也一定要是"xxx_i2c",只有这样当在加载驱动或者设备的时候,才能互相匹配到。

    2.编写设备驱动

    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/delay.h>
    #include <linux/ide.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/gpio.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/of_gpio.h>
    #include <linux/semaphore.h>
    #include <linux/timer.h>
    #include <linux/i2c.h>
    #include <asm/mach/map.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    #define XXX_I2C_CNT     1            /*设备数*/
    #define XXX_I2C_NAME    "xxx_i2c"    /*设备名字*/
    
    /*设备结构体*/
    struct _xxx_i2c_dev
    {
        dev_t devid;            /*设备号*/
        int major;
        int minor;
        struct cdev cdev;
        struct class *class;
        struct device *device;
        //struct device_node *nd;    /*设备节点*/
        void *private_data;        /*私有数据*/
    };
    
    struct _xxx_i2c_dev xxx_i2c_dev;
    
    
    static int xxx_i2c_open(struct inode *inode, struct file *filp)
    {
        filp->private_data = &xxx_i2c_dev;
    
        return 0;
    }
    
    
    static ssize_t xxx_i2c_read(struct file *filp, char __user *buf, size_t size, loff_t *loft)
    {
    
    
        return 0;
    }
    
    
    
    static ssize_t xxx_i2c_write(struct file *filp, const char __user *buf, size_t size, loff_t *loft)
    {
    
    
        return 0;
    }
    
    
    static int xxx_i2c_release(struct inode *inode, struct file *filp)
    {
    
        return 0;
    }
    
    
    const struct file_operations xxx_i2c_fops = {
        .owner   = THIS_MODULE,
        .open    = xxx_i2c_open,    
        .read    = xxx_i2c_read,
        .write   = xxx_i2c_write,
        .release = xxx_i2c_release,
    };
    
    
    int xxx_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        printk(KERN_WARNING "xxx_i2c_probe
    ");
        /*1.创建设备号*/
        if(xxx_i2c_dev.major)
        {
            xxx_i2c_dev.devid = MKDEV(xxx_i2c_dev.major, 0);
            register_chrdev_region(xxx_i2c_dev.devid, XXX_I2C_CNT, XXX_I2C_NAME);/*注册一个字符设备*/
        }
        else
        {
            alloc_chrdev_region(&xxx_i2c_dev.devid, 0, XXX_I2C_CNT, XXX_I2C_NAME);
            xxx_i2c_dev.major = MAJOR(xxx_i2c_dev.devid);
            xxx_i2c_dev.minor = MINOR(xxx_i2c_dev.devid);
        }
        
        /*2.初始化、注册设备*/
        cdev_init(&xxx_i2c_dev.cdev, &xxx_i2c_fops);
        cdev_add(&xxx_i2c_dev.cdev, xxx_i2c_dev.devid, XXX_I2C_CNT);
    
        /*3.创建类*/
        xxx_i2c_dev.class = class_create(THIS_MODULE, XXX_I2C_NAME);
    
        if(IS_ERR(xxx_i2c_dev.class))
        {
            printk(KERN_WARNING "Create class failed
    ");
            
            return PTR_ERR(xxx_i2c_dev.class);
        }
    
        /*4.创建设备*/
        xxx_i2c_dev.device = device_create(xxx_i2c_dev.class, NULL, xxx_i2c_dev.devid, NULL, XXX_I2C_NAME);
    
        if(IS_ERR(xxx_i2c_dev.device))
        {
            printk(KERN_WARNING "Create device failed
    ");
    
            return PTR_ERR(xxx_i2c_dev.device);
        }
    
        return 0;
    }
    
    int xxx_i2c_remove(struct i2c_client *client)
    {
        /*1.删除设备*/
        cdev_del(&xxx_i2c_dev.cdev);
        /*2.注销设备*/
        device_destroy(xxx_i2c_dev.class, xxx_i2c_dev.devid);
        class_destroy(xxx_i2c_dev.class);
    
        return 0;
    }
    
    
    /*匹配表,用于非设备树的情况下的i2c设备*/
    static const struct i2c_device_id xxx_i2c_id[] = {
        {"xxx_i2c", 0},/*名字要和板级文件里面的设备名字相对应*/
        {},
    };
    
    MODULE_DEVICE_TABLE(i2c, xxx_i2c_id);//通过 MODULE_DEVICE_TABLE 声明一下 xxx_i2c_id设备匹配表
    
    /*驱动结构体*/
    static struct i2c_driver xxx_i2c_drv = {
        .probe = xxx_i2c_probe,
        .remove = xxx_i2c_remove,
        .driver = {
              .owner = THIS_MODULE,
              .name  = "xxx_i2c",
              },
        .id_table = xxx_i2c_id,//用于设备和驱动匹配,设备属性在板级文件中有初始化,比如设备的名字
    };
    
    
    static int __init xxx_i2c_drv_init(void)
    {
        int val = 0;
    
        val = i2c_add_driver(&xxx_i2c_drv);/*添加驱动*/
    
        if(val != 0){
            printk(KERN_WARNING "Add xxx_i2c_drv failed
    ");
        }
        
        return 0;
    }
    
    
    static void __exit xxx_i2c_drv_exit(void)
    {
        i2c_del_driver(&xxx_i2c_drv);
    }
    
    
    module_init(xxx_i2c_drv_init);
    module_exit(xxx_i2c_drv_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("xxx");

    编写完成后make一下,把编译数来的ko文件拷贝到文件系统中,运行命令进行驱动的安装

    #depmod

    #modprobe my_i2c.ko

    成功安装后,查看/dev路径下的设备,即可看到我们刚添加的设备xxx_i2c

  • 相关阅读:
    No resource found that matches the given name 'Theme.AppCompat.Light' 的完美解决方案
    Data Flow ->> Fuzzy Lookup & Fuzzy Grouping
    SSIS ->> Script Debugging and Troubleshooting
    Data Flow ->> Script Component
    SSIS ->> Logging
    SSIS ->> Event Handler
    SSIS ->> Script Task
    Data Flow ->> Look up & Merge Join
    SSIS ->> 生成时间格式
    SSIS ->> Null & Null Functions
  • 原文地址:https://www.cnblogs.com/timemachine213/p/12953385.html
Copyright © 2020-2023  润新知