九、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\n");
    /*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\n");
        
        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\n");

        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\n");
    }
    
    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

猜你喜欢

转载自www.cnblogs.com/timemachine213/p/12953385.html