文章目录
- 1.platform总线:相对于USB/PCI/I2C/SPI等物理总线来说,platform总线是一种虚拟的总线,实际并不存在,总线的工作就是就是完成总线下的设备和驱动之间的匹配。也就是在左手中找到与右手相匹配的设备驱动,并完成他们之间的匹配
- 1.1 of_device_id + of_match_table设备树采用的匹配方式:设备树中的每个设备节点的compatible属性会和 of_match_table表中的所有成员比较,查看是否有相同的条目,有的话就表示设备和此驱动匹配,设备和驱动匹配成功后probe函数会执行
- 1.2 id_table匹配:id_table不存在的话就直接比较驱动和设备name字段
- 2.I2C协议:总线空闲的时SCL和SDA处于高电平。 I2C总线标准模式下速度可达100Kb/S,快速模式下可达400Kb/S
- 2.1 I2C读时序(i2cget):sda线上ack和data是slave发,其他都是master发
- 2.2 I2C写时序(i2cset):主机通过I2C总线与从机之间进行通信只有写和读,sda线上只有ack是slave发,其他都是master发
- 3.I2C驱动:两不同的IIC设备挂在同一个IIC控制器下,给这两个设备编写驱动程序部分代码重复,因此linux将IIC驱动分层
- 3.1 I2C总线&适配器:每一组i2c总线对应一个i2c控制器即i2c_adapter,一个芯片可有多个控制器
- 3.2 I2C驱动&设备:一个设备对应一个i2c_client,每检测到一个I2C设备就会给这个I2C设备分配一个i2c_client
- 4.I2C设备添加:系统中对i2c_client的注册是在i2c_adapter的注册过程中完成
- 4.1 用户空间注册:删除设备只能删除在用户空间创建的i2c设备
- 4.2 静态注册:i2c_board_info结构体或dts来配置硬件信息,调用i2c_register_board_info函数来向设备链表来注册硬件设备,然后系统在注册i2c_adapter完成后,取出链表中的每一个设备,当设备的i2c的编号和i2c_adapter相同时,在调用device_register对此设备注册
- 案例:i2c_new_device(不管i2c设备是否真的存在,都实例化i2c_client),i2c_new_probed_device(调用probe函数去探测i2c地址是否有回应,存在则实例化i2c_client)
- 4.3 动态注册:前面都事先指定适配器(i2c设备挂到哪一条i2c总线上),动态要配合设备驱动,硬件信息写到设备驱动中
1.platform总线:相对于USB/PCI/I2C/SPI等物理总线来说,platform总线是一种虚拟的总线,实际并不存在,总线的工作就是就是完成总线下的设备和驱动之间的匹配。也就是在左手中找到与右手相匹配的设备驱动,并完成他们之间的匹配
// include/linux/device.h, Linux系统内核使用bus_type结构体表示总线, Linux系统中文件的体现形式是在/sys/bus下有spi文件夹里有device和driver文件夹
struct bus_type {
const char *name; /* 总线名字 */
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups; /* 总线属性 */
const struct attribute_group **dev_groups; /* 设备属性 */
const struct attribute_group **drv_groups; /* 驱动属性 */
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
// drivers/base/platform.c
struct bus_type platform_bus_type = { // platform总线是bus_type的一个具体实例
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match, //下面定义
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
// 如下4种设备和驱动匹配方式:
/* 1. Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* 2. Then try ACPI style match */
// 从device中找到acpi_device设备后就可匹配了,of_driver_match_device没有匹配到,则用这种
if (acpi_driver_match_device(dev, drv))
return 1;
/* 3. Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 4. fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
platform总线管理下的platform_device和platform_driver(任何一种Linux设备驱动模型下的总线都由这两个部分组成)。
// include/linux/platform_device.h
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
struct platform_driver {
int (*probe)(struct platform_device *); /// 当驱动与设备匹配成功以后 probe 函数就会执行
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; device_driver基类下面定义
const struct platform_device_id *id_table; // id_table是个表(也就是数组),每个元素的类型为 platform_device_id,下面定义
bool prevent_deferred_probe;
};
//struct platform_device_id {
// char name[PLATFORM_NAME_SIZE];
// kernel_ulong_t driver_data;
//};
1.1 of_device_id + of_match_table设备树采用的匹配方式:设备树中的每个设备节点的compatible属性会和 of_match_table表中的所有成员比较,查看是否有相同的条目,有的话就表示设备和此驱动匹配,设备和驱动匹配成功后probe函数会执行
// include/linux/of_device.h
static inline int of_driver_match_device(struct device *dev, // platform_match函数中调用
const struct device_driver *drv) device_driver下面定义
{
return of_match_device(drv->of_match_table, dev) != NULL;
}
struct device_driver { //基类,platform_driver继承了这个基类
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table; of_device_id下面定义
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
//struct of_device_id { //include/linux/mod_devicetable.h
// char name[32];
// char type[32];
// char compatible[128];
// const void *data;
//};
of_device_id不是i2c_device_id,dts中匹配如下:/sys/bus/i2c/devices/3-0050下产生eeprom节点,psu用24c02。
1.2 id_table匹配:id_table不存在的话就直接比较驱动和设备name字段
2.I2C协议:总线空闲的时SCL和SDA处于高电平。 I2C总线标准模式下速度可达100Kb/S,快速模式下可达400Kb/S
2.1 I2C读时序(i2cget):sda线上ack和data是slave发,其他都是master发
1.
主机发送起始信号。
2.
主机发送要读取的I2C从设备地址。#######
3.
读写控制位,因为是向I2C从设备发送数据,因此是写信号。
4.
从机发送的ACK应答信号。
5.
重新发送START信号。
6.
主机发送要读取的寄存器地址。 #######
7.
从机发送的 ACK 应答信号。
8.
重新发送START信号。
9.
重新发送要读取的I2C从设备地址。########比写时序多出的
10.
读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
11.
从机发送的ACK应答信号。
12.
从I2C器件里面读取到的数据。#######
13.
主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
14.
主机发出STOP信号,停止I2C通信。
逻辑分析仪一个接SDA,一个接SCL,一个接地。
2.2 I2C写时序(i2cset):主机通过I2C总线与从机之间进行通信只有写和读,sda线上只有ack是slave发,其他都是master发
1.
开始信号。
2.
发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位(设备地址不用管)是读写位。 ####### i2cget等用的7位,不用偏移
3.
I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
4.
从机发送的ACK应答信号。
5.
重新发送开始信号。
6.
发送要写写入数据的寄存器地址(也叫command)。 ########
7.
从机发送的ACK应答信号
8.
发送要写入寄存器的数据。 #######多字节读写在这多发数据
9.
从机发送的ACK应答信号。
10.
停止信号。
3.I2C驱动:两不同的IIC设备挂在同一个IIC控制器下,给这两个设备编写驱动程序部分代码重复,因此linux将IIC驱动分层
3.1 I2C总线&适配器:每一组i2c总线对应一个i2c控制器即i2c_adapter,一个芯片可有多个控制器
// 设备数据结构i2c_client中的一个对象指向i2c_adapter,这样就可以通过其中的方法(i2c_adapter内部的i2c_algorithm对象)以及i2c物理控制器来和一个i2c总线的物理设备进行交互。
struct i2c_adapter {
struct module *owner;//所有者
unsigned int class; /*适配器支持的从设备类型 */
const struct i2c_algorithm *algo; /* 适配器的通信方法,也就是下面的i2c_algorithm */
void *algo_data; //algo私有数据
int timeout; /* 超时时间 */
int retries; //重传次数,在通信方法中使用
struct device dev; /* 表明这是一个设备,挂载在I2C总线上 */
int nr; //适配器(总线)的编号
char name[48]; //适配器名称
struct list_head userspace_clients; //IIC设备的client依附链表头,只有在用户层echo name addr > /sys/bus/i2c/devices/i2c-x/new_device 时候创建的client才会依附在此链表
…//省略部分无关成员
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); //I2C传输函数指针,i2c_transfer函数的底层调用
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); //smbus传输函数指针,SMBUS协议发送函数,i2c_smbus_xxx函数的底层调用
u32 (*functionality) (struct i2c_adapter *); /* 这个IIC适配器支持什么样的功能,比如支持SMBUS字节发送或读取操作,标志位为I2C_FUNC_SMBUS_BYTE_DATA */
};
// i2c总线(/sys/bus/i2c)注册I2C驱动,将I2C驱动添加到I2C总线的驱动链表中: 遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数,i2c_device_probe函数会调用I2C驱动的probe函数
struct bus_type i2c_bus_type = { // I2C总线结构体在linux-aspeed/drivers/i2c/i2c-core-base.c
.name = "i2c", //总线的名称
.match = i2c_device_match, //
.probe = i2c_device_probe, //
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
3.2 I2C驱动&设备:一个设备对应一个i2c_client,每检测到一个I2C设备就会给这个I2C设备分配一个i2c_client
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated; //老的匹配函数
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *); //当前匹配成功后执行函数,一般是申请资源,注册字符驱动
int (*remove)(struct i2c_client *); //当前移除设备或驱动时执行的移除函数,一般是释放资源
int (*probe_new)(struct i2c_client *); ///未来匹配成功后的执行函数
void (*shutdown)(struct i2c_client *); //关闭设备
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver; /device_driver基类,如果使用设备树的话,需要设置device_driver的of_match_table 成员变量,也就是驱动的兼容(compatible)属性。
const struct i2c_device_id *id_table; /id_table是传统未使用设备树的设备匹配ID表
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list; //该驱动所支持的所有(次设备)的地址数组,用于注册驱动时自动去匹配
struct list_head clients; //用来挂接与该i2c_driver匹配成功的i2c_client (次设备)的一个链表头
bool disable_i2c_core_irq_mapping;
};
struct i2c_client { // 一般通过i2c_new_device()创建
unsigned short flags; // I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错
unsigned short addr; /* IIC设备基地址,7bit。这里说一下为什么是7位,因为最后1位0表示写,1表示读,通过对这个7bit地址移位处理即可。addr<<1 & 0x0即写,addr<<1 | 0x01即读, 对应上面的I2C协议 */
char name[I2C_NAME_SIZE]; // iic设备名字,用于匹配iic驱动
struct i2c_adapter *adapter; /* iic设备是挂在哪个适配器(总线)上 */
struct device dev; /* 表明这是一个设备,挂在I2C总线上 */
int irq; /* 中断号,用于申请中断,一般是在自己实现的iic驱动的probe函数中使用*/
struct list_head detected; //是i2c_adapter结构体或i2c_driver结构体中链表的节点
};
struct i2c_board_info { // 基本与i2c_client对应
char type[I2C_NAME_SIZE]; //设备名,最长20个字符,最终安装到client的name上
unsigned short flags; //最终安装到client.flags
unsigned short addr; //设备从地址slave address,最终安装到client.addr上
void *platform_data; //设备数据,最终存储到i2c_client.dev.platform_data上
struct dev_archdata *archdata;
struct device_node *of_node; //OpenFirmware设备节点指针
struct acpi_dev_node acpi_node;
int irq; //设备采用的中断号,最终存储到i2c_client.irq上
};
4.I2C设备添加:系统中对i2c_client的注册是在i2c_adapter的注册过程中完成
4.1 用户空间注册:删除设备只能删除在用户空间创建的i2c设备
root@bmc:/sys/bus/i2c/devices/i2c-0# echo 0xd > delete_device //删除bus0上0x0d设备,里面0-000d文件夹被删除
root@bmc:/sys/bus/i2c/devices/i2c-0# rmmod basecpld //驱动,这行lsmod查看不到,如果这行比上行先执行,0-000d文件夹还在,但里面节点没了
root@bmc:/sys/bus/i2c/devices/i2c-0# insmod ~/basecpld.ko //驱动
root@bmc:/sys/bus/i2c/devices/i2c-0# echo basecpld 0xd > new_device //设备,将ko放到对应路径(/lib/modules/4.1.51/extra/basecpld.ko),这行执行后,不用上行insmod
root@bmc:/sys/bus/i2c/drivers/lm75# ls // lm75是驱动名即ko名, a.ko则a就是驱动名 , lsmod查看
37-004a 54-0049 55-0048 7-0048 7-0049 7-004b bind module uevent unbind
root@bmc:/sys/bus/i2c/drivers# ls // 和上面/sys/bus/i2c/devices/i2c-0 区分设备
ads7828 fcbcpld lm75 ocmcpld rtc-ds3232
at24 fpga_driver maccpld pca954x rtc-pcf85063
basecpld i2c-slave-mqueue max1363 psu_driver tmp401
dummy ina3221 net_brcm pxe1410 xdpe12284
4.2 静态注册:i2c_board_info结构体或dts来配置硬件信息,调用i2c_register_board_info函数来向设备链表来注册硬件设备,然后系统在注册i2c_adapter完成后,取出链表中的每一个设备,当设备的i2c的编号和i2c_adapter相同时,在调用device_register对此设备注册
如下是没有dts:arm架构board-xxx-yyy.c(架构板级)文件或x86架构下xxx-yyy-init-zzz.c(初始化)文件。
static struct i2c_board_info my_tmp75_info = {
I2C_BOARD_INFO("my_tmp75", 0x48), // 设备名,设备地址
};
// busnum:哪一条总线,也就是选择哪一个i2c控制器(adapter)
// info:i2c设备信息,就是上面的结构体
// n:info中有几个设备
// 如下函数在linux-aspeed/drivers/i2c/i2c-boardinfo.c
i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n)
{
devinfo->busnum = busnum; /* 组装i2c总线 */
devinfo->board_info = *info; /* 绑定设备信息 */
list_add_tail(&devinfo->list, &__i2c_board_list); /* 将上面定义的i2c设备信息添加进__i2c_board_list链表中 */
}
如下出现了dts之后,为了去耦合,将这些的板级信息全部都定义在设备树(arch/arm/boots/dts),linux在启动uboot的时会自动展开dts上的硬件信息,自动调用i2c_register_board_info函数将设备注册进设备链表中,如下第2组总线地址0x01c…(i2c总线相对于芯片的cpu而言也是设备)。
如上两种方式将硬件设备添加到设备链表之后,如下内核是怎么向i2c总线动态注册这些设备的:
// 如下都在linux-aspeed/drivers/i2c/i2c-core-base.c,i2c_add_numbered_adapter调用i2c_register_adapter
// 注册i2c控制器(adapter)时,将会去查找这个__i2c_board_list链表,并实例化i2c设备添加到i2c总线上
static int i2c_register_adapter(struct i2c_adapter *adap)
{
...
dev_set_name(&adap->dev, "i2c-%d", adap->nr); 设置adap->dev.kobj.name为i2c-x ,它将出现在sysfs中
adap->dev.bus = &i2c_bus_type; //此设备依附的总线类型是i2c总线类型;
adap->dev.type = &i2c_adapter_type; //此设备的设备类型是i2c_adapter;
res = device_register(&adap->dev); //注册i2c_adapter设备;
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap); // 下面定义
...
}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) { // __i2c_board_list链表
if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
dev_err(&adapter->dev,"Can't create device at 0x%02x\n",devinfo->board_info.addr);
} // i2c_new_device调用i2c_new_client_device
up_read(&__i2c_board_lock);
}
struct i2c_client *
i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
...
client->adapter = adap; //要创建的i2c_client依附到当前的i2c_adapter控制器上
client->addr = info->addr; //设置设备的地址,从__i2c_board_list链表取得的信息,传参struct i2c_board_info const *info
client->init_irq = info->irq; //中断编号默认为0,在启动设备中通过gpio_to_irq会对irq重新赋值
strlcpy(client->name, info->type, sizeof(client->name)); //对i2c_client的名字赋值,靠这名字和驱动的id_table进行匹配
status = device_register(&client->dev);
...
}
EXPORT_SYMBOL_GPL(i2c_new_client_device);
案例:i2c_new_device(不管i2c设备是否真的存在,都实例化i2c_client),i2c_new_probed_device(调用probe函数去探测i2c地址是否有回应,存在则实例化i2c_client)
// 如下使用 i2c_new_device 注册设备:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
static struct i2c_board_info my_tmp75_info = {
I2C_BOARD_INFO("my_tmp75", 0x48),
};
static struct i2c_client *my_tmp75_client;
static int my_tmp75_init(void)
{
struct i2c_adapter *i2c_adapt;
int ret = 0;
i2c_adapt = i2c_get_adapter(6);
if (i2c_adapt == NULL)
{
printk("get adapter fail!\n");
ret = -ENODEV;
}
/
my_tmp75_client = i2c_new_device(i2c_adapt, &my_tmp75_info); // i2c_board_info
if (my_tmp75_client == NULL)
{
printk("i2c new fail!\n");
ret = -ENODEV;
}
i2c_put_adapter(i2c_adapt);
return ret;
}
static void my_tmp75_exit(void)
{
i2c_unregister_device(my_tmp75_client);
}
module_init(my_tmp75_init);
module_exit(my_tmp75_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("aaa");
MODULE_DESCRIPTION("This my i2c device for tmp75");
// 如下使用 i2c_new_probed_device 注册设备:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
static struct i2c_client *my_tmp75_client;
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须以I2C_CLIENT_END宏结尾
static int my_i2c_dev_init(void)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info my_i2c_dev_info;
memset(&my_i2c_dev_info, 0, sizeof(struct i2c_board_info));
strlcpy(my_i2c_dev_info.type, "my_tmp75", I2C_NAME_SIZE);
i2c_adap = i2c_get_adapter(0);
my_tmp75_client = i2c_new_probed_device(i2c_adap, &my_i2c_dev_info, addr_list, NULL);//只会匹配0x48地址
i2c_put_adapter(i2c_adap);
if (my_tmp75_client)
return 0;
else
return -ENODEV;
}
static void my_i2c_dev_exit(void)
{
i2c_unregister_device(my_tmp75_client);
}
module_init(my_i2c_dev_init);
module_exit(my_i2c_dev_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("aaa");
MODULE_DESCRIPTION("This my i2c device for tmp75");
// 驱动中probe函数
int my_driver_probe(void)
{
struct i2c_adapter *adapter;
struct i2c_board_info info = { };
struct i2c_client *client;
int ret;
unsigned short normal_i2c[] = { 0xd, I2C_CLIENT_END };
struct list_head *pos;
const char *name = "ocmcpld";
struct i2c_client **client_list;
strlcpy(info.type, name, sizeof(info.type));
info.addr = 0x0d;
adapter = i2c_get_adapter(1); // 获取i2c adapter
if (!adapter){
printk(KERN_CRIT "Failed i2c_get_adapter\n");
return -1;
}
client = i2c_new_device(adapter, &info); // 创建i2c设备
if (!client) {
printk(KERN_CRIT "Failed to create i2c client\n");
return -1;
}
client = i2c_new_probed_device(adapter, &info, &normal_i2c, NULL); // 注掉 info.addr = 0x0d;
if (!client) {
printk(KERN_CRIT "Failed to find i2c client\n");
return -1;
}
// client = i2c_find_by_name("ocmcpld");
// if (!client) {
// printk(KERN_CRIT "Failed to find i2c client\n");
// return -1;
// }
// list_for_each(pos, adapter->client_list) {
// client = list_entry(pos, struct i2c_client, list);
// if (client->addr == 0xd && strcmp(client->name, "ocmcpld") == 0) {
// printk(KERN_INFO "client find\n"); //找到指定设备的i2c_client结构体
// break;
// }
// }
ret = i2c_smbus_read_byte_data(client, 0x00);
if (ret < 0) {
printk(KERN_CRIT "Read failed\n");
return -1;
}
printk(KERN_INFO "ocmcpld ret:0x%x\n",ret);
return 0;
}
4.3 动态注册:前面都事先指定适配器(i2c设备挂到哪一条i2c总线上),动态要配合设备驱动,硬件信息写到设备驱动中
insmod .ko时会执行module_init中xxx_init入口函数会调用如下:
// linux-aspeed/drivers/i2c/i2c-core-base.c
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (WARN_ON(!is_registered))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; //设定这个driver依附的总线
INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver); //这函数会调用其他函数对driver和device进行匹配,若匹配成功,i2c_register_driver返回res,结束执行;如不成功,则会往下执行i2c_for_each_dev函数(这函数接下来调用的其他函数会动态注册i2c设备)
if (res)
return res;
pr_debug("driver [%s] registered\n", driver->driver.name);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver); //注意传参
return 0;
} // 至此驱动注册就完成了,注意不是设备
// int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data))
// {
// int res;
// mutex_lock(&core_lock);
// res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); //注意最后两个传参:driver, __process_new_driver
// mutex_unlock(&core_lock);
// return res;
// }
// // 只如下这一个函数在linux-aspeed/drivers/base/bus.c中
// int bus_for_each_dev(struct bus_type *bus, struct device *start,
// void *data, int (*fn)(struct device *, void *))
// {
// struct klist_iter i;
// struct device *dev;
// int error = 0;
// if (!bus || !bus->p)
// return -EINVAL;
// klist_iter_init_node(&bus->p->klist_devices, &i,
// (start ? &start->p->knode_bus : NULL)); //遍历链表上的所有设备
// while (!error && (dev = next_device(&i)))
// error = fn(dev, data); //通过next_device函数将链表上的设备dev取出然后传递给fn即__process_new_driver函数,data就是driver
// klist_iter_exit(&i);
// return error;
// }
// __process_new_driver拿到driver和device做了什么?
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type) //判断拿到的设备是否是i2c总线设备即是不是i2c_adapter
return 0;
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
static int i2c_do_add_adapter(struct i2c_driver *driver,
struct i2c_adapter *adap)
{
i2c_detect(adap, driver);
return 0;
}
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
int adap_id = i2c_adapter_id(adapter); //获取i2c_adapter设备的i2c总线号
address_list = driver->address_list; //获取设备驱动程序中定义的address_list
if (!driver->detect || !address_list) //判断驱动程序中是定义了detect 函数和address_list,其中一个没定义,则失败返回0;
return 0;
/* Warn that the adapter lost class based instantiation */
if (adapter->class == I2C_CLASS_DEPRECATED) {
dev_dbg(&adapter->dev,
"This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. "
"If you need it, check 'Documentation/i2c/instantiating-devices.rst' for alternatives.\n",
driver->driver.name);
return 0;
}
// adapter->class = 1; // 或 = I2C_CLASS_HWMON;
/* Stop here if the classes do not match */
if (!(adapter->class & driver->class)) { //这里在判断驱动程序中定义的class和adapter中定义的class是否相同,不相同则失败返回0;&:有0为0
printk("000, %d, %d\n", adapter->class , driver->class);
return 0; } // 注意加括号
/* Set up a temporary client to help detect callback */
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!temp_client)
return -ENOMEM;
temp_client->adapter = adapter; //这里定义了一个i2c_client,下面的程序会对这个进行赋值,然后再注册
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
printk("111, %d, %d, %d\n", i , address_list[i], temp_client->adapter->nr); //nr为bus号
dev_dbg(&adapter->dev,
"found normal entry for adapter %d, addr 0x%02x\n",
adap_id, address_list[i]);
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (unlikely(err))
break;
}
kfree(temp_client);
return err;
}
static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_driver *driver)
{
struct i2c_board_info info; //这里定义了结构体i2c_board_info,看了静态注册设备的话,对这个结构体很熟悉了,没有使用dts的时候必须要用i2c_board_info 结构体存放硬件信息,动态注册也一样,只是定义这个结构体的位置不同而已。
struct i2c_adapter *adapter = temp_client->adapter;
int addr = temp_client->addr;
int err;
/* Make sure the address is valid */
err = i2c_check_7bit_addr_validity_strict(addr); // 检测i2c地址是否7位有效
if (err) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return err;
}
/* Skip if already in use (7 bit, no need to encode flags) */
if (i2c_check_addr_busy(adapter, addr)) // 跳过已经使用的i2c设备
return 0;
/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr)) // 检查这个地址是否有回应
return 0;
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr; // 这里把在i2c_detect函数中检测到的i2c地址赋值给了info这个结构体,并将temp_client和info一起传入了驱动程序中定义的detect函数,
err = driver->detect(temp_client, &info); // 驱动程序中detect函数
if (err) {
return err == -ENODEV ? 0 : err;
}
if (info.type[0] == '\0')
{
}
else
{
struct i2c_client *client;
client = i2c_new_device(adapter, &info); // 驱动中的detect函数必须要填写i2c_board_info结构体中name,i2c_new_device才能实例化i2c设备
if (client)
printk("222, creating %s at 0x%02x\n", info.type, info.addr);
list_add_tail(&client->detected, &driver->clients); // 只有这种方式添加的i2c设备才会挂在驱动的链表上
}
}
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
static int __devinit my_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return 0;
}
static int __devexit my_i2c_drv_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id my_dev_id_table[] = {
{ "my_i2c_dev", 0 },
{}
};//这里的名字很重要,驱动第一种匹配设备的方式要用到
static int my_i2c_drv_detect(struct i2c_client *client, struct i2c_board_info *info)
{
/* 能运行到这里, 表示该addr的设备是存在的
* 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
* 还需要进一步读写I2C设备来分辨是哪款芯片,自己写方法
* detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type,也就是设备名字
*/
printk("my_i2c_drv_detect: addr = 0x%x\n", client->addr);
/* 进一步判断是哪一款 */
strlcpy(info->type, "my_i2c_dev", I2C_NAME_SIZE);
return 0;
}
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须使用I2C_CLIENT_END宏结尾
/* 分配/设置i2c_driver */
static struct i2c_driver my_i2c_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备,不是对应类将不会调用匹配, 适配器初始化时,也会初始化class,驱动在探测设备时,会将适配器的class和i2c_driver结构体的class比较 */
.driver = {
.name = "my_i2c_dev",
.owner = THIS_MODULE,
},
.probe = my_i2c_drv_probe, // 当I2C设备和I2C驱动匹配时,调用该函数
.remove = __devexit_p(my_i2c_drv_remove), // 当I2C设备移除时,调用该函数
.id_table = my_dev_id_table,
.detect = my_i2c_drv_detect, /* 用这个函数来检测address_list里设备确实存在 ,填充设备名字,调用i2c_new_device注册i2c_client,如果i2c_board_info.type和i2c_driver.id_table.name一致调用probe函数*/
.address_list = addr_list, /* 这些设备的地址 */
};
static int my_i2c_drv_init(void)
{
i2c_add_driver(&my_i2c_driver);
return 0;
}
static void my_i2c_drv_exit(void)
{
i2c_del_driver(&my_i2cc_driver);
}
module_init(my_i2c_drv_init);
module_exit(my_i2c_drv_exit);
MODULE_LICENSE("GPL");