在linux中的i2c分层结构,也是代码的分层结构。

这张图片是从程序调用角度的i2c结构图,其中左侧的/sys和/dev也就是用户访问设备的方式。

代码调用层次图

  有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容

上面这些代码的展示是告诉我们:linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。
  剩下的就是针对挂载在i2c两线上的i2c设备了device,而编写的即具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)

架构层次分类

  第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层

  第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层

  第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层

  第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层

  第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。

  在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的(与特定硬件相关的代码)已由芯片提供商编写完成,例如三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c

第三第四层与特定device相干的就需要驱动工程师来实现了。

设备驱动

设备驱动就是特定的器件的设备驱动,其中有两个主要的结构体

i2c_driver(i2c设备驱动结构体)

struct i2c_driver {  unsigned int class;  int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针  int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针  int (*probe)(struct i2c_client *, const struct i2c_device_id *);  int (*remove)(struct i2c_client *);  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);  int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表  struct device_driver driver;  const struct i2c_device_id *id_table;//该驱动所支持的设备ID表  int (*detect)(struct i2c_client *, struct i2c_board_info *);  const unsigned short *address_list;  struct list_head clients;
}; 

i2c_client(i2c设备描述结构体)

struct i2c_client {  unsigned short flags;//标志    unsigned short addr; //低7位为芯片地址    char name[I2C_NAME_SIZE];//设备名称  struct i2c_adapter *adapter;//依附的i2c_adapter  struct i2c_driver *driver;//依附的i2c_driver   struct device dev;//设备结构体    int irq;//设备所使用的结构体    struct list_head detected;//链表头
};  

添加设备驱动函数

static inline int i2c_add_driver(struct i2c_driver *driver)
{  return i2c_register_driver(THIS_MODULE, driver);
}

删除设备驱动函数

void i2c_del_driver (struct i2c_driver * driver);

i2c-core核心层

i2c总线的初始化、注册和适配器的添加和注销

适配器驱动

I2c控制器的驱动实现

I2c消息结构体

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 */  #define I2C_M_NOSTART       0x4000   /* if I2C_FUNC_PROTOCOL_MANGLING */  #define I2C_M_REV_DIR_ADDR  0x2000   /* if I2C_FUNC_PROTOCOL_MANGLING */  #define I2C_M_IGNORE_NAK    0x1000   /* if I2C_FUNC_PROTOCOL_MANGLING */  #define I2C_M_NO_RD_ACK 0x0800   /* if I2C_FUNC_PROTOCOL_MANGLING */  #define I2C_M_RECV_LEN 0x0400   /* length will be first received byte */  __u16 len;      /* msg length               */  __u8 *buf;      /* pointer to msg data           */
};  

i2c核心提供api函数

i2c传输、发送和接收

int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);

i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{  int ret;  if (adap->algo->master_xfer) {//如果master_xfer函数存在,则调用,否则返回错误  ret = adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值  return ret;  } else {  return -ENOSYS;  }
}
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{ int ret; struct i2c_adapter *adap=client->adapter;  // 获取adapter信息 struct i2c_msg msg;                        // 定义一个临时的数据包 msg.addr = client->addr;                   // 将从机地址写入数据包 msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包 msg.len = count;                           // 将此次发送的数据字节数写入数据包 msg.buf = (char *)buf;                     // 将发送数据指针写入数据包 ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口发送数据 /* If everything went ok (i.e. 1 msg transmitted), return #bytestransmitted, else error code. */ return (ret == 1) ? count : ret;           // 如果发送成功就返回字节数
}
EXPORT_SYMBOL(i2c_master_send);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{ struct i2c_adapter *adap=client->adapter;  // 获取adapter信息 struct i2c_msg msg;                        // 定义一个临时的数据包 int ret; msg.addr = client->addr;                   // 将从机地址写入数据包 msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包 msg.flags |= I2C_M_RD;                     // 将此次通信的标志并入数据包 msg.len = count;                           // 将此次接收的数据字节数写入数据包 msg.buf = buf; ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口接收数据 /* If everything went ok (i.e. 1 msg transmitted), return #bytestransmitted, else error code. */ return (ret == 1) ? count : ret;           // 如果接收成功就返回字节数
}
EXPORT_SYMBOL(i2c_master_recv);

I2c适配器结构体i2c_adapter

struct i2c_adapter {  struct module *owner;//所属模块  unsigned int id;//algorithm的类型,定义于i2c-id.h,  unsigned int class;      const struct i2c_algorithm *algo; //总线通信方法结构体指针  void *algo_data;//algorithm数据  /* --- administration stuff. */  int (*client_register)(struct i2c_client *);  int (*client_unregister)(struct i2c_client *);  /* data fields that are valid for all devices   */  struct rt_mutex bus_lock;//控制并发访问的自旋锁  struct mutex clist_lock;  int timeout;     int retries;//重试次数  struct device dev; //适配器设备   struct class_device class_dev;  /* the class device */  int nr;  struct list_head clients;  struct list_head list;  char name[I2C_NAME_SIZE];//适配器名称  struct completion dev_released;//用于同步  struct list_head userspace_clients;//client链表头
};  

编写驱动需要完成的工作

  编写具体的I2C驱动时,工程师需要处理的主要工作如下:

  1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。

  2).提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。

  3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。

  4).实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接。

上面的工作中前两个属于I2C总线驱动,后面两个属于I2C设备驱动。

AT24C02驱动示例

客户驱动程序开发的一般步骤

(1)注册板载i2c设备信息

(2)定义i2c驱动设备id

(3)定义i2c_driver结构并完成其相应函数

(4)模块初始化时添加/撤销时删除i2c_driver

(5)/dev entry 访问方法 /sysfs访问方法

其中1,2为device,3,4,5为driver

(1)注册板载信息

在mach-smdk2440.c文件中静态声明一个I2C设备

static struct i2c_board_info i2c_devices[] __initdata = {  {I2C_BOARD_INFO("24c02", 0x50), },  {}
};

在smdk2440_machine_init()函数中,向总线注册I2C设备信息:

i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices)); 

(2)定义i2c驱动设备id:

static struct i2c_device_id foo_idtable[] = {  { “24c01", 0x51 },  {“20402”,0x50 },  {}
};

(3)定义i2c_driver结构并完成其相应函数:

static struct i2c_driver my_i2c_driver = {  .driver = {  .name = "i2c_demo", .owner = THIS_MODULE,  },  .probe = my_i2c_probe,  .remove = my_i2c_remove,  .id_table = my_ids,
};  

(4)模块初始化时添加/撤销时删除i2c_driver

static int __init  my_i2c_client(void)
{  return i2c_add_driver(&my_i2c_driver);
}
static void __exit my_i2c_exit(void)
{  i2c_del_driver(&my_i2c_driver);
}

(5)使用/dev entry 访问方法

注册字符设备

register_chrdev(I2C_MAJOR,DEVICE_NAME,&i2c_fops); 

创建类

class_create(THIS_MODULE, DEVICE_NAME); 

在/dev下创建设备节点

device_create(my_dev_class,&client->dev,MKDEV(I2C_MAJOR, 0), NULL, DEVICE_NAME);  

在/doc/Documentation/i2c/instantiating-devices文档中提供了4中方法实例化一个iic设备

上面提到的是一个,下面介绍另外三个

第二种方法

(1)      定义i2c设备信息,i2c_board_info结构体

(2)      在device文件中init函数中使用i2c_new_device或者i2c_new_probed_device()

i2c_new_device        : 认为设备肯定存在

i2c_new_probed_device :对于"已经识别出来的设备"(probed_device),才会创建("new")

int at24cxx_dev_init(void)
{struct i2c_adapter * i2c_adap;i2c_adap=i2c_get_adapter(0);at24cxx_client=i2c_new_device(i2c_adap,&at24cxx_info);i2c_put_adapter(i2c_adap);i2c_put_adapter(i2c_adap);return 0;
}
void at24cxx_dev_exit(void)
{i2c_unregister_device(at24cxx_client);
}

3,4,5和方法一一样

第三种方法

从用户空间创建设备

创建设备

echo name addr >/sys/bus/i2c/devices/i2c-3/new_device

如echo eeprom 0x50 > /sys/class/i2c-adapter/i2c-0/new_device

删除设备

如echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device

第四种方法

Device用1,2完成

(3)      在driver中i2c_driver结构体中

static struct i2c_driver at24cxx_driver = {.class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */.driver    = {.name   = "100ask",.owner    = THIS_MODULE,},.probe     = at24cxx_probe,.remove        = __devexit_p(at24cxx_remove),.id_table    = at24cxx_id_table,.detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */.address_list   = addr_list,   /* 这些设备的地址 */
};

4,5和方法一相同

Linux中I2C子系统框架相关推荐

  1. linux内核I2C子系统详解

    1.I2C通信协议 参考博客:<I2C通信协议详解和通信流程分析>:https://csdnimg.cn/release/blogv2/dist/pc/themesSkin/skin3-t ...

  2. linux中spi驱动框架

    原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...

  3. Linux的 i2c 驱动框架分析

    1.基本概念 总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工 ...

  4. Linux下I2C驱动框架全面解析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  5. 嵌入式Linux中I2C设备驱动程序的研究与实现

    I2C是"Inter Integrated Circuit Bus"的缩写,中文译成"内部集成电路总线", 它是Philips 公司于20 世纪80 年代研发成 ...

  6. linux内核I2C子系统学习(三)

    写设备驱动: 四部曲: 构建i2c_driver 注册i2c_driver 构建i2c_client ( 第一种方法:注册字符设备驱动.第二种方法:通过板文件的i2c_board_info填充,然后注 ...

  7. Linux中通过v4l2框架获取摄像头的能力的方法

    v4l2(video for linux two)是Linux中内核提供给应用层访问音视频驱动的统一接口.v4l2中获取摄像头的能力的是通过ioctl函数的VIDIOC_QUERYCAP命令获取,并且 ...

  8. linux内核I2C子系统学习(二)

    下面具体分析如何写第一部分: 主控芯片的i2c驱动分为2个步骤: 写总线驱动: 选了个主控芯片,比如:S3C8900(自己瞎编的芯片) 在driver/i2c/busses/i2c-s3c2410.c ...

  9. linux内核I2C子系统学习(一)

    这部分准备分几个部分进行分析总结 因为I2C的通信肯定至少要有2个芯片完成,所以它的驱动是由2大部分组成: 主芯片的i2c的驱动 从芯片的i2c的驱动     注:万一选的都不支持咋办???(惨了,只 ...

最新文章

  1. 飞越难关,飞书生态「战疫工具箱」来驰援!
  2. Ubuntu 16.04下的LAMP环境配置
  3. 修改属性页CPropertyPage标题
  4. VS 2019 要来了,是时候了解一下 C# 8.0 新功能
  5. Django 实现第三方账号登录网站
  6. ASP.NET前台table通过Ajax获取绑定后台查询的json数据
  7. Codeforces 1043F(容斥+dp)
  8. Linux valgrind java_Ubuntu下使用valgrind所遇问题
  9. 谷歌浏览器整个网页截图方法
  10. XML内容要有根标签:Extra content at the end of the document
  11. 使用CDN后网页无法访问怎么解决
  12. 惊!成年蚂蚁竟然返老还童!原因居然是。。。。
  13. matlab状态空间模型构建函数ss
  14. Python3输出格式化时间yyyy-mm-dd HH:MM:SS
  15. 新款自助机存在的故障隐患及解决方法
  16. 14-射频校准的原理和设置
  17. 【数据结构】8. 队列(带头节点的单链表实现)(完整代码实现:初始化、入队列、出队列、获取队头元素、获取队尾元素、获取队列中有效元素的个数、判空、销毁)
  18. mailbox的controller
  19. VB、VBS 、ASP、VBA 的 UTF-8 MD5 实现
  20. 会员系统_健身房管理系统

热门文章

  1. python之Tkinter界面创建
  2. 三妖怪,三和尚过河问题(美图2017线下笔试题)
  3. pyecharts 大小_pyecharts 常用API 图形初始化
  4. 万亿移动支付产业的难点和痛点
  5. 贴片陶瓷电容器材质NPO、COG、X7R、X5R、Y5V、Z5U区别
  6. GitHub注册及使用
  7. 【报错解决】To search for alternate channels that may provide the conda package you‘relooking for, naviga
  8. 移动互联网(一)短信和彩信界面开发包
  9. Cocos Creator与两位兄弟“合体”啦!|视频解说
  10. SCJP复习规划for 1.4