IIC特点
- IIC是一种半双工基础协议
- IIC一次传输8bit,高位在前,低位在后
- 总线空闲状态时 数据线为高电平
IIC总线架构分析
设备驱动层组件 linux*/drivers/i2c/i2c-dev.c
- 提供用户层相关接口
- 统一封装发送格式,但不关心数据的发送形式
设备核心层组件 linux*/drivers/i2c/i2c-core.c
- 注册I2C总线
- 为驱动层提供编程接口
设备总线驱动层组件 linux*/drivers/i2c/busses/i2c-xxx.c 不同芯片实现不同
- 初始化硬件相关接口如IIC控制器
- 实现数据发送形式,如高位发送,不关心具体发送的数据
这三个文件的目的是为了相互配合实现操作相应的外围设备
IIC核心层分析
核心层文件 i2c-core-base.c
postcore_initcall(i2c_init); i2c注册函数
i2c_init函数分析
/* 注册i2c设备,最终形成一个i2c的驱动链表 */
static int __init i2c_init(void)
{
// 注册一根i2c物理总线
retval = bus_register(&i2c_bus_type); ----|
|
struct bus_type i2c_bus_type = { <--------|
.name = "i2c",
.match = i2c_device_match, // 匹配函数 ------------------------|
.probe = i2c_device_probe, |
.remove = i2c_device_remove, |
.shutdown = i2c_device_shutdown, |
}; |
static int i2c_device_match(struct device *dev, struct device_driver *drv)<---|
{
struct i2c_client *client = i2c_verify_client(dev); // i2c设备
struct i2c_driver *driver; // i2c驱动
// 获取驱动
driver = to_i2c_driver(drv);
// 根据id_table名称和设备结构体匹配,如果匹配则返回匹配的id,否则返回空
/* Finally an I2C match */
if (i2c_match_id(driver->id_table, client))
return 1;
return 0;
}
// 注册i2c驱动
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
is_registered = false;
bus_unregister(&i2c_bus_type);
return retval;
}
Linux驱动涉及到的重要结构体i2c.h
i2c驱动描述结构体 表示一个i2c驱动
struct i2c_driver {
检测函数
int (*probe)(struct i2c_client *client);
void (*remove)(struct i2c_client *client);
struct device_driver driver; // 表示驱动
const struct i2c_device_id *id_table; // 记录i2c驱动能服务于哪些设备
};
i2c设备描述结构体 表示一个i2c设备
struct i2c_client {
unsigned short flags; /* 唯一标号 */
unsigned short addr; /* 设备地址 7bit */
char name[I2C_NAME_SIZE]; /* 设备名称 */
struct i2c_adapter *adapter; /* 所属适配器 */
struct device dev; /* 设备结构体 */
int init_irq; /* 设备对应的中断号 */
int irq; /* irq issued by device */
struct list_head detected; // 链表
};
i2c适配器/控制器
struct i2c_adapter {
const struct i2c_algorithm *algo; /* 表示i2c的操作方法,如利用i2c的协议传输数据 */
};
i2c数据发送
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
};
i2c_msg i2c数据包
struct i2c_msg {
__u16 addr; // 设备地址
__u16 flags; // 数据包读写标志位 1 读 0 写
__u16 len; // 数据包有效数据长度
__u8 *buf; // 有效数据指针
};
i2c_board_info i2c板卡信息
struct i2c_board_info {
char type[I2C_NAME_SIZE]; // i2c设备名称 如 cv1800
unsigned short flags;
unsigned short addr; // 设备地址
const char *dev_name;
void *platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct software_node *swnode;
const struct resource *resources;
unsigned int num_resources;
int irq;
};
操作时使用宏来操作
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
IIC设备驱动层分析 i2c-dev.c
初始化函数 i2c_dev_init
- 注册主设备号和硬件操作方法
- 创建主设备类
- 找到设备并关联驱动
- 创建设备文件
static int __init i2c_dev_init(void)
{
/* 注册主设备号,注册硬件操作方法 */
/* I2C_MAJOR 在系统中主设备号为89 */
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
/* 创建设备类 */
i2c_dev_class = class_create("i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
/* 绑定适配器,绑定具体操作的哪一台设备,搜索I2C总线的设备链表,
每搜索到一个设备,都会调用i2cdev_attach_adapter */
i2c_for_each_dev(NULL, i2c_dev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
pr_err("Driver Initialisation failed\n");
return res;
}
static int i2cdev_attach_adapter(struct device *dev)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
/* 判断是否是适配器,如果不是则返回 */
if (dev->type != &i2c_adapter_type)
return NOTIFY_DONE;
i2c_dev = get_free_i2c_dev(adap);
/* 初始化一个i2c设备链表节点 */
device_initialize(&i2c_dev->dev);
/* 设置i2c设备名称 /dev/i2c-0 dev/i2c-1... */
i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
i2c_dev->dev.class = i2c_dev_class;
i2c_dev->dev.parent = &adap->dev;
i2c_dev->dev.release = i2cdev_dev_release;
}
iic开发涉及到的设备层的重要函数
int i2c_master_send(const struct i2c_client *client,
const char *buf, int count) 发送函数 字节发送
int i2c_master_recv(const struct i2c_client *client,
char *buf, int count) 接收函数 字节接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); 传输一个i2c数据包
int i2c_register_board_info(int busnum, struct i2c_board_info const *info,
unsigned n); 注册i2c板级信息,如i2c的oled屏幕名称
int i2c_add_adapter(struct i2c_adapter *adap); 注册/控制器
void i2c_del_adapter(struct i2c_adapter *adap); 注销/控制器
int i2c_add_numbered_adapter(struct i2c_adapter *adap);
注册/注销i2c驱动
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);
注册/注销i2c设备