文章目录

  • 前言
  • 一、Linux内核对I2C总线的支持
    • 1.1、理解I2C设备驱动、I2C总线驱动以及I2C核心之间的关系
    • 1.2、i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:
  • 二、I2C总线二级外设驱动开发方法-名称匹配
    • 2.1、i2c二级外设client框架:
    • 2.2、i2c二级外设驱动框架
    • 2.3、I2C总线二级外设驱动-名称匹配相关代码
  • 三、I2C总线二级外设驱动开发方法-设备树匹配
    • 3.1、I2C总线二级外设驱动-设备树匹配相关代码

前言

记录嵌入式驱动学习笔记


一、Linux内核对I2C总线的支持

1.1、理解I2C设备驱动、I2C总线驱动以及I2C核心之间的关系


I2C设备驱动:
即挂接在I2C总线上的二级外设的驱动,也称客户(client)驱动,实现对二级外设的各种操作,二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写,对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收。
I2C总线驱动:
即对I2C总线自身控制器的驱动,一般SOC芯片都会提供多个I2C总线控制器,每个I2C总线控制器提供一组I2C总线(SDA一根+SCL一根),每一组被称为一个I2C通道,Linux内核里将I2C总线控制器叫做适配器(adapter),适配器驱动主要工作就是提供通过本组I2C总线与二级外设进行数据传输的接口,每个二级外设驱动里必须能够获得其对应的adapter对象才能实现数据传输。
I2C核心:
承上启下,为I2C设备驱动和I2C总线驱动开发提供接口,为I2C设备驱动层提供管理多i2c_driver、i2c_client对象的数据结构,为I2C总线驱动层提供多个i2c_algorithm、i2c_adapter对象的数据结构。

1.2、i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:

i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:

struct i2c_board_info {    char        type[I2C_NAME_SIZE];    unsigned short  flags;    unsigned short  addr;    void        *platform_data;    struct dev_archdata *archdata;    struct device_node *of_node;    int     irq;};/*用来协助创建i2c_client对象 相当于总线平台的devices重要成员type:用来初始化i2c_client结构中的name成员flags:用来初始化i2c_client结构中的flags成员 置0表示7位的从设备地址addr:用来初始化i2c_client结构中的addr成员 从设备地址platform_data:用来初始化i2c_client结构中的.dev.platform_data成员archdata:用来初始化i2c_client结构中的.dev.archdata成员irq:用来初始化i2c_client结构中的irq成员关键就是记住该结构和i2c_client结构成员的对应关系。在i2c子系统不直接创建i2c_client结构,只是提供struct i2c_board_info结构信息,让子系统动态创建,并且注册。*/
struct i2c_client {    unsigned short flags;    unsigned short addr;    char name[I2C_NAME_SIZE];    struct i2c_adapter *adapter;    struct i2c_driver *driver;    struct device dev;    int irq;    struct list_head detected;};/*重要成员:flags:地址长度,如是10位还是7位地址,默认是7位地址。如果是10位地址器件,则设置为I2C_CLIENT_TENaddr:具体I2C器件如(at24c02),设备地址,低7位name:设备名,用于和i2c_driver层匹配使用的,可以和平台模型中的平台设备层platform_driver中的name作用是一样的。adapter:本设备所绑定的适配器结构(CPU有很多I2C适配器,类似单片机有串口1、串口2等等,在linux中每个适配器都用一个结构描述)driver:指向匹配的i2c_driver结构,不需要自己填充,匹配上后内核会完成这个赋值操作dev:内嵌的设备模型,可以使用其中的platform_data成员传递给任何数据给i2c_driver使用。irq:设备需要使用到中断时,把中断编号传递给i2c_driver进行注册中断,如果没有就不需要填充。(有的I2C器件有中断引脚编号,与CPU相连)*//* 获得/释放 i2c_adapter 路径:i2c-core.c linux-3.5\drivers\i2c *//*功能:通过i2c总线编号获得内核中的i2c_adapter结构地址,然后用户可以使用这个结构地址就可以给i2c_client结构使用,从而实现i2c_client进行总线绑定,从而增加适配器引用计数。返回值:NULL:没有找到指定总线编号适配器结构非NULL:指定nr的适配器结构内存地址*/ //从设备获取i2c通道 0~7一共8通道struct i2c_adapter *i2c_get_adapter(int nr);/*减少引用计数:当使用·i2c_get_adapter·后,需要使用该函数减少引用计数。(如果你的适配器驱动不需要卸载,可以不使用)*/void i2c_put_adapter(struct i2c_adapter *adap);/*功能:根据参数adap,info,addr,addr_list动态创建i2c_client并且进行注册参数:adap:i2c_client所依附的适配器结构地址info:i2c_client基本信息addt_list: i2c_client的地址(地址定义形式是固定的,一般是定义一个数组,数组必须以I2C_CLIENT_END结束,示例:unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END};probe:回调函数指针,当创建好i2c_client后,会调用该函数,一般没有什么特殊需求传递NULL。返回值:非NULL:创建成功,返回创建好的i2c_client结构地址NULL:创建失败*///在众多地址中找一个可以匹配上的 不清楚设备地址值时使用struct i2c_client * i2c_new_probed_device( struct i2c_adapter *adap, struct i2c_board_info *info, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *, unsigned short addr));/*示例:struct i2c_adapter *ad;struct i2c_board_info info={""};unsigned short addr_list[]={0x38,0x39,I2C_CLIENT_END};//假设设备挂在i2c-2总线上ad=i2c_get_adapter(2);//自己填充board_info strcpy(inf.type,"xxxxx");info.flags=0;//动态创建i2c_client并且注册//在众多地址中找一个可以匹配上的 不清楚设备地址值时使用i2c_new_probed_device(ad,&info,addr_list,NULL);i2c_put_adapter(ad);*//*注销*/void i2c_unregister_device(struct i2c_client *pclt)//明确知道从设备地址值时使用该函数 struct i2c_client * i2c_new_device (     struct i2c_adapter *padap,     struct i2c_board_info const *pinfo );/*示例:struct i2c_adapter *ad;struct i2c_board_info info={I2C_BOARD_INFO(name,二级外设地址)};//假设设备挂在i2c-2总线上ad=i2c_get_adapter(2);//动态创建i2c_client并且注册i2c_new_device(ad,&info);i2c_put_adapter(ad);*/
struct i2c_driver {    unsigned int class;    /* Standard driver model interfaces */    int (*probe)(struct i2c_client *, const struct i2c_device_id *);    int (*remove)(struct i2c_client *);    /* driver model interfaces that don't relate to enumeration  */    void (*shutdown)(struct i2c_client *);    int (*suspend)(struct i2c_client *, pm_message_t mesg);    int (*resume)(struct i2c_client *);void (*alert)(struct i2c_client *, unsigned int data);    /* a ioctl like command that can be used to perform specific functions     * with the device.     */    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);    struct device_driver driver;    const struct i2c_device_id *id_table;    /* Device detection callback for automatic device creation */    int (*detect)(struct i2c_client *, struct i2c_board_info *);    const unsigned short *address_list;    struct list_head clients;};/*重要成员:probe:在i2c_client与i2c_driver匹配后执行该函数remove:在取消i2c_client与i2c_driver匹配绑定后后执行该函数driver:这个成员类型在平台设备驱动层中也有,而且使用其中的name成员来实现平台设备匹配,但是i2c子系统中不使用其中的name进行匹配,这也是i2c设备驱动模型和平台设备模型匹配方法的一点区别id_table:用来实现i2c_client与i2c_driver匹配绑定,当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候,就匹配上了。补充:i2c_client与i2c_driver匹配问题- i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候- i2c_client指定的信息在物理上真实存放对应的硬件,并且工作是正常的才会绑定上,并执行其中的probe接口函数这第二点要求和平台模型匹配有区别,平台模型不要求设备层指定信息在物理上真实存在就能匹配*//*功能:向内核注册一个i2c_driver对象返回值:0成功,负数 失败*/#define i2c_add_driver(driver)     i2c_register_driver(THIS_MODULE, driver)int i2c_register_driver(struct module *owner, struct i2c_driver *driver);/*功能:从内核注销一个i2c_driver对象返回值:无 */void i2c_del_driver(struct i2c_driver *driver);
struct i2c_msg {    __u16 addr; /* slave address            */    __u16 flags;#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */#define I2C_M_RD        0x0001  /* read data, from slave to master */    __u16 len;      /* msg length               */    __u8 *buf;      /* pointer to msg data          */};/* 重要成员:addr:要读写的二级外设地址flags:表示地址的长度,读写功能。如果是10位地址必须设置I2C_M_TEN,如果是读操作必须设置有I2C_M_RD······,可以使用或运算合成。buf:要读写的数据指针。写操作:数据源 读操作:指定存放数据的缓存区len:读写数据的数据长度*//*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*/int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)/*功能:根据msgs进行手法控制参数:adap:使用哪一个适配器发送信息,一般是取i2c_client结构中的adapter指针作为参数msgs:具体发送消息指针,一般情况下是一个数组num:表示前一个参数msgs数组有多少个消息要发送的返回值:负数:失败> 0 表示成功发送i2c_msg数量*//*I2C读取数据函数*/int i2c_master_recv(const struct i2c_client *client, char *buf, int count)/*功能:实现标准的I2C读时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+读方向这一环节了参数:client:设备结构buf:读取数据存放缓冲区count:读取数据大小 不大于64k返回值:失败:负数成功:成功读取的字节数*/    /*I2C发送数据函数*/int i2c_master_send(const struct i2c_client *client, const char *buf, int count)/*功能:实现标准的I2C写时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+写方向这一环节了参数:client:设备结构地址buf:发送数据存放缓冲区count:发送数据大小 不大于64k返回值:失败:负数成功:成功发送的字节数*/

二、I2C总线二级外设驱动开发方法-名称匹配

2.1、i2c二级外设client框架:

#include #include #include static struct i2c_board_info mpu6050_info = {I2C_BOARD_INFO("mpu6050",二级外设地址)   };static struct i2c_client *mpu6050_client;static int __init mpu6050_dev_init(void){    struct i2c_adapter *padp = NULL;    //引用计数加一    padp = i2c_get_adapter(i2c通道编号);    //根据通道和mpu6050_info创建一个mpu6050_client对象    mpu6050_client = i2c_new_device(padp,&mpu6050_info);    //引用计数减一    i2c_put_adapter(padp);    return 0;}module_init(mpu6050_dev_init);static void __exit mpu6050_dev_exit(void){    i2c_unregister_device(mpu6050_client);}module_exit(mpu6050_dev_exit);MODULE_LICENSE("GPL");

2.2、i2c二级外设驱动框架

//其它struct file_operations函数实现原理同硬编驱动static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid){    //做硬编驱动模块入口函数的活}static int mpu6050_remove(struct i2c_client *pclt){    //做硬编驱动模块出口函数的活}//因为可以匹配多个所以是个数组/*名称匹配时定义struct i2c_device_id数组*/static struct i2c_device_id mpu6050_ids = {    {"mpu6050",0},    //.....    {}};/*设备树匹配时定义struct of_device_id数组*/static struct of_device_id mpu6050_dts ={    {.compatible = "invensense,mpu6050"},    //....    {}};/*通过定义struct i2c_driver类型的全局变量来创建i2c_driver对象,同时对其主要成员进行初始化*/struct i2c_driver mpu6050_driver = {.driver = {        .name = "mpu6050",        .owner = THIS_MODULE,        //设备树匹配        .of_match_table = mpu6050_dts,    },    .probe = mpu6050_probe,    .remove = mpu6050_remove,    //id匹配    .id_table = mpu6050_ids,};/*以下其实是个宏,展开后相当于实现了模块入口函数和模块出口函数*/module_i2c_driver(mpu6050_driver);MODULE_LICENSE("GPL");

2.3、I2C总线二级外设驱动-名称匹配相关代码

mpu6050.h

#ifndef MPU_6050_H#define MPU_6050_Hstruct accel_data{unsigned short x;unsigned short y;unsigned short z;};struct gyro_data{unsigned short x;unsigned short y;unsigned short z;};union mpu6050_data{struct accel_data accel;struct gyro_data gyro;unsigned short temp;};#define MPU6050_MAGIC 'K'#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)#endif

mpu6050_client.c

#include #include #include //二级外设信息static struct i2c_board_info mpu6050_info = {//与驱动模块i2c名称一致 从设备地址I2C_BOARD_INFO("mpu6050",0x68)};//定义i2c_client类型的全局指针static struct i2c_client *gpmpu6050_client = NULL;//模块入口函数static int __init mpu6050_client_init(void){struct i2c_adapter *padp = NULL;//i2c5号 增加引用计数padp = i2c_get_adapter(5);//创建client对象 已知i2c二级外设gpmpu6050_client = i2c_new_device(padp,&mpu6050_info);//减去引用计数i2c_put_adapter(padp);return 0;}//模块出口函数static void  mpu6050_client_exit(void){//删除client对象i2c_unregister_device(gpmpu6050_client);}module_init(mpu6050_client_init);module_exit(mpu6050_client_exit);MODULE_LICENSE("GPL");

mpu6050_drv.c

#include #include #include #include #include #include #include #include #include #include #include #include #include //ioctl头文件#include "mpu6050.h"//mpu6050相关寄存器#define SMPLRT_DIV 0x19#define CONFIG 0x1A#define GYRO_CONFIG 0x1B#define ACCEL_CONFIG 0x1C#define ACCEL_XOUT_H 0x3B#define ACCEL_XOUT_L 0x3C#define ACCEL_YOUT_H 0x3D#define ACCEL_YOUT_L 0x3E#define ACCEL_ZOUT_H 0x3F#define ACCEL_ZOUT_L 0x40#define TEMP_OUT_H 0x41#define TEMP_OUT_L 0x42#define GYRO_XOUT_H 0x43#define GYRO_XOUT_L 0x44#define GYRO_YOUT_H 0x45#define GYRO_YOUT_L 0x46#define GYRO_ZOUT_H 0x47#define GYRO_ZOUT_L 0x48#define PWR_MGMT_1  0x6B//mpu6050驱动程序int major = 11;int minor = 0;int mpu6050_num  = 1;struct mpu6050_dev{//表示为一个字符设备struct cdev mydev;//匹配成功的devices的地址struct i2c_client *pclt;};struct mpu6050_dev *pgmydev = NULL;//i2c读 1. i2c_client对象地址 2.要读的寄存器地址int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg){//接收i2c_transfer函数返回值 判断读取是否成功int ret = 0;//寻址操作所需要的传输buf 里面填要读取寄存器的地址char txbuf[1] = {reg};//读取buf char rxbuf[1] = {0};struct i2c_msg msg[2] = {//mpu6050地址 七位设备地址长度 buf长度为1 读取寄存器的地址 完成寻址{pclt->addr,0,1,txbuf},//mpu6050地址 读数据的宏 buf长度为1 读取到该buf内{pclt->addr,I2C_M_RD,1,rxbuf}};/*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*//*功能:根据msgs进行手法控制参数:adap:使用哪一个适配器发送信息,一般是取i2c_client结构中的adapter指针作为参数msgs:具体发送消息指针,一般情况下是一个数组num:表示前一个参数msgs数组有多少个消息要发送的返回值:负数:失败> 0 表示成功发送i2c_msg数量*/ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));if(ret < 0){printk("ret = %d,in mpu6050_read_byte\n",ret);return ret;}return rxbuf[0];}//i2c写 1. i2c_client对象地址 2.要写的寄存器地址 3.要写入的数据int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val){int ret = 0;//写数据 要写入的寄存器  要写入的数据char txbuf[2] = {reg,val};//写数据只需要一个msgstruct i2c_msg msg[1] = {//mpu6050地址 七位设备地址长度 buf长度为2 要写入的buf内容{pclt->addr,0,2,txbuf},};ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));if(ret < 0){printk("ret = %d,in mpu6050_write_byte\n",ret);return ret;}return 0;}int mpu6050_open(struct inode *pnode,struct file *pfile){pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));return 0;}int mpu6050_close(struct inode *pnode,struct file *pfile){return 0;}//通过ioctl来获取mpu6050数据 long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg){struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;//mpu6050数据结构体 在头文件里 union mpu6050_data data;//仅实现了读取到局部变量data里 需要发送给应用层 通过 copy to userswitch(cmd){case GET_ACCEL:data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);data.accel.x |= mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);data.accel.y |= mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);data.accel.z |= mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);data.gyro.x |= mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);data.gyro.y |= mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);data.gyro.z |= mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;break;case GET_TEMP:data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);data.temp = |mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;break;default:return -EINVAL;}//强转成(void *) 返回非0表示失败if(copy_to_user((void *)arg,&data,sizeof(data))){return -EFAULT;}return sizeof(data);}//mpu6050初始化 寄存器void init_mpu6050(struct i2c_client *pclt){mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);}struct file_operations myops = {.owner = THIS_MODULE,.open = mpu6050_open,.release = mpu6050_close,.unlocked_ioctl = mpu6050_ioctl,};//匹配成功调用//参数是 1.i2c_client对象的地址  2.i2c_device_id匹配成功的devices idstatic int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid){int ret = 0;dev_t devno = MKDEV(major,minor);/*申请设备号*/ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");if(ret){ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);if(NULL == pgmydev){unregister_chrdev_region(devno,mpu6050_num);printk("kmalloc failed\n");return -1;}memset(pgmydev,0,sizeof(struct mpu6050_dev));//驱动一旦跟client设备匹配上 就可以通过这个成员变量得到对应的client对象pgmydev->pclt = pclt;/*给struct cdev对象指定操作函数集*/cdev_init(&pgmydev->mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/pgmydev->mydev.owner = THIS_MODULE;cdev_add(&pgmydev->mydev,devno,mpu6050_num);//初始化mpu器件init_mpu6050(pgmydev->pclt);return 0;}//参数是i2c_client对象的地址static int mpu6050_remove(struct i2c_client *pclt){dev_t devno = MKDEV(major,minor);cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,mpu6050_num);kfree(pgmydev);pgmydev = NULL;return 0;}/*名称匹配时定义struct i2c_device_id数组*/struct i2c_device_id mpu6050_ids[] = {{"mpu6050",0},//表示数组结束{}};/*通过定义struct i2c_driver类型的全局变量来创建i2c_driver对象,同时对其主要成员进行初始化*/struct i2c_driver mpu6050_driver = {.driver = {.name = "mpu6050",.owner = THIS_MODULE,},.probe = mpu6050_probe,.remove = mpu6050_remove,//名称只能1对1 id匹配可以一对多.id_table = mpu6050_ids,};#if 0int __init mpu6050_driver_init(void){//向内核注册一个i2c_driver对象i2c_add_driver(&mpu6050_driver);}void __exit mpu6050_driver_exit(void){//从内核注销一个i2c_driver对象i2c_del_driver(&mpu6050_driver);}module_init(mpu6050_driver_init);module_exit(mpu6050_driver_exit);#else/*以下其实是个宏,展开后相当于实现了模块入口函数和模块出口函数*/module_i2c_driver(mpu6050_driver);#endifMODULE_LICENSE("GPL");

三、I2C总线二级外设驱动开发方法-设备树匹配

3.1、I2C总线二级外设驱动-设备树匹配相关代码

向设备数添加i2c设备节点,.h文件与名称匹配中的.h文件相同

i2c@138B0000 {#address-cells = <1>;#size-cells = <0>;samsung,i2c-sda-delay = <100>;samsung,i2c-max-bus-ferq = <20000>;pinctrl-0 = <&i2c5_bus>;pinctrl-names = "default";status = "okay";mpu6050-3-asix@68 {compatible = "invensense,mpu6050";reg = <0x68>;interrupt-parent = <&gpx3>;interrupts = <3 2>;};};

mpu6050_drv.c

#include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpu6050.h"#define SMPLRT_DIV 0x19#define CONFIG 0x1A#define GYRO_CONFIG 0x1B#define ACCEL_CONFIG 0x1C#define ACCEL_XOUT_H 0x3B#define ACCEL_XOUT_L 0x3C#define ACCEL_YOUT_H 0x3D#define ACCEL_YOUT_L 0x3E#define ACCEL_ZOUT_H 0x3F#define ACCEL_ZOUT_L 0x40#define TEMP_OUT_H 0x41#define TEMP_OUT_L 0x42#define GYRO_XOUT_H 0x43#define GYRO_XOUT_L 0x44#define GYRO_YOUT_H 0x45#define GYRO_YOUT_L 0x46#define GYRO_ZOUT_H 0x47#define GYRO_ZOUT_L 0x48#define PWR_MGMT_1  0x6Bint major = 11;int minor = 0;int mpu6050_num  = 1;struct mpu6050_dev{struct cdev mydev;struct i2c_client *pclt;};struct mpu6050_dev *pgmydev = NULL;int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg){int ret = 0;char txbuf[1] = {reg};char rxbuf[1] = {0};struct i2c_msg msg[2] = {{pclt->addr,0,1,txbuf},{pclt->addr,I2C_M_RD,1,rxbuf}};ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));if(ret < 0){printk("ret = %d,in mpu6050_read_byte\n",ret);return ret;}return rxbuf[0];}int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val){int ret = 0;char txbuf[2] = {reg,val};struct i2c_msg msg[1] = {{pclt->addr,0,2,txbuf},};ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));if(ret < 0){printk("ret = %d,in mpu6050_write_byte\n",ret);return ret;}return 0;}int mpu6050_open(struct inode *pnode,struct file *pfile){pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));return 0;}int mpu6050_close(struct inode *pnode,struct file *pfile){return 0;}long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg){struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;union mpu6050_data data;switch(cmd){case GET_ACCEL:data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);data.accel.x |= mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);data.accel.y |= mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);data.accel.z |= mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);data.gyro.x |= mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);data.gyro.y |= mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);data.gyro.z |= mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;break;case GET_TEMP:data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);data.temp |= mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;break;default:return -EINVAL;}if(copy_to_user((void *)arg,&data,sizeof(data))){return -EFAULT;}return sizeof(data);}void init_mpu6050(struct i2c_client *pclt){mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);}struct file_operations myops = {.owner = THIS_MODULE,.open = mpu6050_open,.release = mpu6050_close,.unlocked_ioctl = mpu6050_ioctl,};static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid){int ret = 0;dev_t devno = MKDEV(major,minor);/*申请设备号*/ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");if(ret){ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);if(NULL == pgmydev){unregister_chrdev_region(devno,mpu6050_num);printk("kmalloc failed\n");return -1;}memset(pgmydev,0,sizeof(struct mpu6050_dev));pgmydev->pclt = pclt;/*给struct cdev对象指定操作函数集*/cdev_init(&pgmydev->mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/pgmydev->mydev.owner = THIS_MODULE;cdev_add(&pgmydev->mydev,devno,mpu6050_num);init_mpu6050(pgmydev->pclt);return 0;}static int mpu6050_remove(struct i2c_client *pclt){dev_t devno = MKDEV(major,minor);cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,mpu6050_num);kfree(pgmydev);pgmydev = NULL;return 0;}//设备树匹配的结构体数组struct of_device_id mpu6050_dt[] = {//通过compatible属性匹配{.compatible = "invensense,mpu6050"},//表示结束{}};//通过设备树匹配 该段程序也必须存在才能生成设备节点struct i2c_device_id mpu6050_ids[] = {{"mpu6050",0},{}};struct i2c_driver mpu6050_driver = {.driver = {.name = "mpu6050",.owner = THIS_MODULE,//增加通过设备树匹配.of_match_table = mpu6050_dt,},.probe = mpu6050_probe,.remove = mpu6050_remove,.id_table = mpu6050_ids,};#if 0int __init mpu6050_driver_init(void){i2c_add_driver(&mpu6050_driver);}void __exit mpu6050_driver_exit(void){i2c_del_driver(&mpu6050_driver);}module_init(mpu6050_driver_init);module_exit(mpu6050_driver_exit);#elsemodule_i2c_driver(mpu6050_driver);#endifMODULE_LICENSE("GPL");