Linux中I2C子系统框架
在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子系统框架相关推荐
- linux内核I2C子系统详解
1.I2C通信协议 参考博客:<I2C通信协议详解和通信流程分析>:https://csdnimg.cn/release/blogv2/dist/pc/themesSkin/skin3-t ...
- linux中spi驱动框架
原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...
- Linux的 i2c 驱动框架分析
1.基本概念 总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工 ...
- Linux下I2C驱动框架全面解析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- 嵌入式Linux中I2C设备驱动程序的研究与实现
I2C是"Inter Integrated Circuit Bus"的缩写,中文译成"内部集成电路总线", 它是Philips 公司于20 世纪80 年代研发成 ...
- linux内核I2C子系统学习(三)
写设备驱动: 四部曲: 构建i2c_driver 注册i2c_driver 构建i2c_client ( 第一种方法:注册字符设备驱动.第二种方法:通过板文件的i2c_board_info填充,然后注 ...
- Linux中通过v4l2框架获取摄像头的能力的方法
v4l2(video for linux two)是Linux中内核提供给应用层访问音视频驱动的统一接口.v4l2中获取摄像头的能力的是通过ioctl函数的VIDIOC_QUERYCAP命令获取,并且 ...
- linux内核I2C子系统学习(二)
下面具体分析如何写第一部分: 主控芯片的i2c驱动分为2个步骤: 写总线驱动: 选了个主控芯片,比如:S3C8900(自己瞎编的芯片) 在driver/i2c/busses/i2c-s3c2410.c ...
- linux内核I2C子系统学习(一)
这部分准备分几个部分进行分析总结 因为I2C的通信肯定至少要有2个芯片完成,所以它的驱动是由2大部分组成: 主芯片的i2c的驱动 从芯片的i2c的驱动 注:万一选的都不支持咋办???(惨了,只 ...
最新文章
- 飞越难关,飞书生态「战疫工具箱」来驰援!
- Ubuntu 16.04下的LAMP环境配置
- 修改属性页CPropertyPage标题
- VS 2019 要来了,是时候了解一下 C# 8.0 新功能
- Django 实现第三方账号登录网站
- ASP.NET前台table通过Ajax获取绑定后台查询的json数据
- Codeforces 1043F(容斥+dp)
- Linux valgrind java_Ubuntu下使用valgrind所遇问题
- 谷歌浏览器整个网页截图方法
- XML内容要有根标签:Extra content at the end of the document
- 使用CDN后网页无法访问怎么解决
- 惊!成年蚂蚁竟然返老还童!原因居然是。。。。
- matlab状态空间模型构建函数ss
- Python3输出格式化时间yyyy-mm-dd HH:MM:SS
- 新款自助机存在的故障隐患及解决方法
- 14-射频校准的原理和设置
- 【数据结构】8. 队列(带头节点的单链表实现)(完整代码实现:初始化、入队列、出队列、获取队头元素、获取队尾元素、获取队列中有效元素的个数、判空、销毁)
- mailbox的controller
- VB、VBS 、ASP、VBA 的 UTF-8 MD5 实现
- 会员系统_健身房管理系统
热门文章
- python之Tkinter界面创建
- 三妖怪,三和尚过河问题(美图2017线下笔试题)
- pyecharts 大小_pyecharts 常用API 图形初始化
- 万亿移动支付产业的难点和痛点
- 贴片陶瓷电容器材质NPO、COG、X7R、X5R、Y5V、Z5U区别
- GitHub注册及使用
- 【报错解决】To search for alternate channels that may provide the conda package you‘relooking for, naviga
- 移动互联网(一)短信和彩信界面开发包
- Cocos Creator与两位兄弟“合体”啦!|视频解说
- SCJP复习规划for 1.4