Linux I2C 框架源码分析

1.i2c 框架源码分析

文章基于

内核版本
linux-3.2.0
CPU
TI 的 am3352
源码路径 kernel-3.2\drivers\i2c\busses\i2c-omap.c
i2c从机设备
Philips PCF8563 RTC
源码路径 kernel-3.2\drivers\rtc\rtc-pcf8563.c

2.linux i2c 代码目录结构

源码路径 kernel-3.2\drivers\i2c

i2c/
|-- busses
|   |-- i2c-gpio.c
|   `-- i2c-omap.c
|-- algos
|   |-- i2c-algo-bit.c
|   |-- i2c-algo-pca.c
|   |-- i2c-algo-pcf.c
|   `-- i2c-algo-pcf.h
|-- muxes
|   `-- gpio-i2cmux.c
|-- i2c-boardinfo.c
|-- i2c-core.c
|-- i2c-core.h
|-- i2c-dev.c
|-- i2c-mux.c
`-- i2c-smbus.c
busses
适配器驱动,对应不同的cpu有不同的适配器驱动,通过对 cpu i2c 寄存器控制 i2c 外设进行协议收发
目录下的 i2c-omap.c 就是 ti335x 系列芯片的适配器驱动,而 i2c-gpio.c 就是通过 gpio 实现的模拟 i2c 协议收发的一个适配器驱动
algos
一般 i2c 算法(如开始/结束/应答/发送)由适配器驱动自行实现,这个目录下提供一些特殊的 i2c 收发算法
像 i2c-algo-bit.c 提供了 i2c-gpio.c 所需的一些算法
muxes
i2c 多路复用的适配器驱动
实际使用上可能有些 i2c 需要多路复用的情况,例如同一个总线上接了多个 i2c 地址相同的 i2c 设备,这时候就需要用到 i2c 多路复用芯片
这个目录提供了 i2c 多路复用芯片的适配器驱动,像 gpio-i2cmux.c 就是利用 gpio 进行片选的 i2c 多路复用芯片的适配器驱动
i2c-boardinfo.c
提供注册 boardinfo 的接口代码
i2c-core.c i2c-core.h
i2c 核心层代码
i2c-dev.c
为注册好的 i2c 总线注册生成设备,将在路径 /dev/ 下生成 i2c-1/i2c-2/i2c-3… 等 i2c 总线设备
为应用提供直接控制总线的方法
i2c-mux.c
i2c 多路复用的核心层代码
i2c-smbus.c
smbus 协议(跟 i2c 协议类似)代码

要了解 i2c 框架,重点看 busses 目录下的适配器驱动和核心层代码 i2c-core.c
其他只是对于框架的一些补充和扩展

3.linux i2c 框架

Linux I2C 框架源码分析_第1张图片
linux i2c 框架分为三层,分别是:

  • i2c核心层
  • i2c适配器层
  • i2c驱动层

i2c读写的一次流程简单来说包括以下步骤:

  1. i2c驱动层组 i2c 消息包,调用i2c核心层接口,进i2c入核心层
  2. i2c核心层回调i2c适配器层已经注册好的 i2c 发送接口,进入i2c适配器层
  3. i2c适配器层通过读写寄存器完成一次与外接i2c设备的读写

3.1.i2c核心层

i2c核心层提供一下功能:

  • 提供适配器注册接口
  • 提供驱动注册接口
  • 提供数据收发接口
  • 管理适配器,对应结构体是 struct i2c_adapter
  • 管理驱动,对应结构体是 struct i2c_client

3.2.i2c适配器层

  • 根据cpu,提供i2c的收发函数,供i2c核心层调用
  • 填充适配器结构体 struct i2c_adapter,调用i2c核心层接口注册进i2c总线

i2c适配器层需要提供 struct i2c_adapter 结构体的 struct i2c_algorithm *algo 字段
该字段是操作i2c总线的算法,源码如下:

struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);

	u32 (*functionality) (struct i2c_adapter *);
};

其中

master_xfer 函数
是 i2c协议收发函数.核心层的 i2c_transfer() 接口回调了这个函数
smbus_xfer 函数
是 smbus协议收发函数,smbus协议跟i2c类似,如果芯片要驱动支持smbus协议的外接芯片,则需要实现这个函数.核心层的 i2c_smbus_xfer() 等接口回调了这个函数
functionality 函数
返回该适配器支持的功能,例如如果支持十位地址,则返回 I2C_FUNC_10BIT_ADDR

3.3.i2c驱动层

  • 根据外接i2c设备需求,实现读写操作,提供write、read、ioctl接口供应用程序调用
  • 读写操作中调用i2c核心层数据收发接口收发数据
  • 调用i2c核心层接口注册驱动进i2c总线

4.适配器层

4.1.平台设备 platform_device

源码: kernel-3.2\arch\arm\mach-omap2\board-am335xevm.c

将 board_info 注册到一条 i2c bus 上
一条 bus 对应一个平台设备
一般会调用几次将 cpu 的全部 i2c 总线都生成对应的适配器平台设备

/*外设I2C设备信息
包含一个与驱动层的平台驱动匹配的设备名称
和一个设备对应的I2C地址*/
static struct i2c_board_info i2c0_boardinfo[] = {
	{
		I2C_BOARD_INFO("tps65217", TPS65217_I2C_ID),
		.platform_data  = &beaglebone_tps65217_info,
	},
   {
            I2C_BOARD_INFO("24c256", 0x50),
   },
   {
            I2C_BOARD_INFO("pcf8563", 0x51),
   },
};

static void __init am335x_evm_i2c_init(void)
	omap_register_i2c_bus(1, 100, i2c0_boardinfo,ARRAY_SIZE(i2c0_boardinfo)); /*将一个/多个I2C外设挂在1号总线(I2C0)*/
    	->进入 int omap_register_i2c_bus(int bus_id, u32 clkrate, struct i2c_board_info const *info, unsigned len)
        i2c_register_board_info(bus_id, info, len); /*注册 board_info*/
        	->进入 int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
            list_add_tail(&devinfo->list, &__i2c_board_list); /*将 board_info 加到 __i2c_board_list 队列,在驱动层的平台设备/驱动匹配时有用到*/
        omap_i2c_add_bus(bus_id);
            omap2_i2c_add_bus(bus_id);
        		->进入 static inline int omap2_i2c_add_bus(int bus_id)
                /*根据 bus_id 找到相关硬件信息 omap_hwmod*/
                ...
                static const char name[] = "omap_i2c";
                omap_device_build(name, ...); /*创建平台设备, name 为 omap_i2c*/

4.2.平台驱动 platform_driver

一个适配器对应一条总线

源码: kernel-3.2\drivers\i2c\busses\i2c-omap.c

通过omap_i2c_init_driver()函数注册一个平台驱动

omap_i2c_init_driver(void) /*适配器平台驱动初始化*/
	platform_driver_register(&omap_i2c_driver); /*注册适配器平台驱动*/
        struct platform_driver omap_i2c_driver = {
                .probe		= omap_i2c_probe,
                .driver		= {
                .name	= "omap_i2c",
            },
        };

适配器层的平台驱动与平台设备名字都是"omap_i2c",匹配后进入probe函数.

omap_i2c_probe(struct platform_device *pdev)
    /*获取适配器平台设备资源,如i2c寄存器地址等*/
    ...
    dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); /*申请驱动结构体 omap_i2c_dev 的内存*/
    omap_i2c_init(dev); /*i2c 总线 0/1/2/3... 的初始化,如时钟初始化*/
	isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :map_i2c_isr; /*设置中断回调,中断里面实现 i2c 协议的收发*/
	request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev); /*申请中断*/
	adap->class = I2C_CLASS_HWMON; /*适配器的 class 设置为 I2C_CLASS_HWMON*/
	strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); /*初始化适配器的 name*/
	adap->algo = &omap_i2c_algo; /*初始化适配器的算法相关函数, 包括收发函数和功能函数,实现cpu的i2c接口的收发*/
		->static const struct i2c_algorithm omap_i2c_algo 定义
        static const struct i2c_algorithm omap_i2c_algo = {
            .master_xfer	= omap_i2c_xfer, /*实现硬件上的收发的函数*/
            .functionality	= omap_i2c_func, /*返回该适配器支持的 i2c 的功能*/
        };
	adap->nr = pdev->id; /*从平台资源获取序号,初始化适配器的序号,对应就是 I2C0/1/2/3..*/
    r = i2c_add_numbered_adapter(adap); /*添加一个 i2c 适配器,进入 i2c 核心层*/
        /*进入 i2c 核心层,参考i2c核心层代码解析*/
        ...

5.驱动层

5.1.平台设备 platform_device

驱动层的平台设备实际上不是在驱动层生成的,它的注册过程如下:

  1. 在适配器层的平台设备注册时调用 i2c_register_board_info() 将 i2c board info 注册到链表 __i2c_board_list
  2. 匹配后,调用 i2c_add_numbered_adapter() 添加一个适配器,进入核心层
  3. 在核心层的 i2c_scan_static_board_info() 函数中调用 i2c_scan_static_board_info() 轮询链表 __i2c_board_list 生成驱动层的平台设备

具体的代码解析参照本文:

  • 适配器层平台设备代码解析
  • 核心层 i2c_add_numbered_adapter() 代码解析

一个 i2c board info 包含平台设备名和地址,设备名用于与平台驱动匹配,如下:

static struct i2c_board_info i2c0_boardinfo[] = {
   {
        I2C_BOARD_INFO("pcf8563", 0x51),
   },
};

实际上,驱动层的平台设备有多种注册的方式,上面步骤只是其中一种.

5.2.平台驱动 platform_driver

源码: kernel-3.2\drivers\rtc\rtc-pcf8563.c

/*驱动信息
提供驱动操作函数和id_table
根据id_table的name字段与平台设备进行匹配*/
static struct i2c_driver pcf8563_driver = {
	.driver		= {
		.name	= "rtc-pcf8563",
	},
	.probe		= pcf8563_probe,
	.remove		= pcf8563_remove,
	.id_table	= pcf8563_id,
};
pcf8563_init(void)
	i2c_add_driver(&pcf8563_driver); /*往 i2c 总线上添加一个驱动*/
	    /*进入 i2c 核心层,参考i2c核心层代码解析*/
	    ...

平台设备与驱动匹配之后进去 probe 函数

static int pcf8563_probe(struct i2c_client *client, const struct i2c_device_id *id)
    i2c_check_functionality(client->adapter, I2C_FUNC_I2C) /*检测i2c总线是否符合驱动要求功能*/
    ...
	pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL);
	...
	rtc_device_register(pcf8563_driver.driver.name, &client->dev, &pcf8563_rtc_ops, THIS_MODULE); /*注册一个rtc设备*/

probe函数注册了一个rtc设备,并提供了下面这些操作接口

static const struct rtc_class_ops pcf8563_rtc_ops = {
	.ioctl		= pcf8563_rtc_ioctl,
	.read_time	= pcf8563_rtc_read_time,
	.set_time	= pcf8563_rtc_set_time,
};

当在终端输入下面的命令时,将进入 pcf8563_rtc_read_time 函数读取打印出当前的 rtc 时间

cat /proc/driver/rtc

static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
	return pcf8563_get_datetime(to_i2c_client(dev), tm);
		->进入 static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
	    /*组 i2c 消息包*/
        struct i2c_msg msgs[] = {
            {/* setup read ptr */
                .addr = client->addr,
                .len = 1,
                .buf = buf
            },
            {/* read status + date */
                .addr = client->addr,
                .flags = I2C_M_RD,
                .len = 13,
                .buf = buf
            },
        };
        ...
        i2c_transfer(client->adapter, msgs, 2) /*发送组包好的 i2c 消息包*/
            /*进入 i2c 核心层,参考i2c核心层代码解析*/
            ...
        /*将读到的数据赋值到 tm 结构体返回*/
        tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
        tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
        tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
        tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
        tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
        tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
        tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
        ...

6.核心层

源码: kernel-3.2\drivers\i2c\i2c-core.c

6.1.i2c_init()

核心层初始化

规定总线的名称/匹配函数/探测函数

struct bus_type i2c_bus_type = {
    .name		= "i2c",
    .match		= i2c_device_match,
    .probe		= i2c_device_probe,
    ...
};

i2c核心层初始化

static int __init i2c_init(void) /*核心层初始化*/
	retval = bus_register(&i2c_bus_type); /*注册一条总线,名为 i2c,将在 /sys/bus/ 生成 i2c 目录*/
    i2c_adapter_compat_class = class_compat_register("i2c-adapter"); /*注册一个 class ,将在 /sys/class/ 生成 i2c-adapter 目录*/
    i2c_add_driver(&dummy_driver); /*注册一个 i2c 驱动,名为 dummy,应该是测试用的*/

6.2.i2c_add_numbered_adapter()

提供给适配器层调用, 注册一个适配器
驱动层 platform_device 在这里完成注册

int i2c_add_numbered_adapter(struct i2c_adapter *adap) /*添加一个 i2c 适配器,进入 i2c 核心层*/
    status = i2c_register_adapter(adap); /*往 i2c 总线注册一个适配器*/
    	->进入 static int i2c_register_adapter(struct i2c_adapter *adap)
        dev_set_name(&adap->dev, "i2c-%d", adap->nr); /*初始化设备名为 i2c-0/1/2/3...*/
        adap->dev.bus = &i2c_bus_type; /*初始化设备的总线类型,该设备归属于 i2c 这条总线*/
        	-> struct bus_type i2c_bus_type 定义
            struct bus_type i2c_bus_type = {
                .name		= "i2c",
                .match		= i2c_device_match,
                .probe		= i2c_device_probe,
                ...
            }
	    adap->dev.type = &i2c_adapter_type; /*初始化设备的类型,该设备为 i2c 适配器类型*/
        res = device_register(&adap->dev);
            /*注册一个设备,该设备为总线设备
            总线名为 i2c ,设备名为 i2c-0/1/2/3...
            将在 /sys/bus/i2c/devices/ 生成 i2c-0/1/2/3...*/
        class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent);
            /*在 i2c_adapter_compat_class 中新建 link
            将在 /sys/class/i2c-adapter/ 生成 i2c-0/1/2/3...*/
        i2c_scan_static_board_info(adap); /*扫描 __i2c_board_list ,根据 board_info 生成所有的驱动层 platform_device*/
        	->进入 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
            list_for_each_entry(devinfo, &__i2c_board_list, list) /*轮询 __i2c_board_list 队列*/
                i2c_new_device(adapter, &devinfo->board_info) /*根据 board_info 生成驱动层 platform_device*/
                	->进入 struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
                    client = kzalloc(sizeof *client, GFP_KERNEL); /*新增一个 i2c_client*/
                    status = i2c_check_client_addr_validity(client); /*检测 i2c 地址有效性*/
                    status = i2c_check_addr_busy(adap, client->addr); /*检查地址是否冲突*/
                    client->dev.bus = &i2c_bus_type; /*设备归属 i2c 总线*/
                    client->dev.type = &i2c_client_type; /*初始化设备的类型,该设备为 i2c 客户端类型,相当于一个节点(适配器可连接多个节点,如i2c下挂多个设备)*/
                    status = device_register(&client->dev); 注册设备
                    	->进入 int device_register(struct device *dev)
                        device_add(dev);
                        	->进入 int device_add(struct device *dev)
                            /*添加device的一些初始化过程*/
                            error = device_create_file(dev, &uevent_attr); 生成对应 uevent 文件
                            if (MAJOR(dev->devt)) {
                                error = device_create_file(dev, &devt_attr);
                                error = device_create_sys_dev_entry(dev);
                                devtmpfs_create_node(dev);
                            }
                            error = device_add_class_symlinks(dev);
                            error = device_add_attrs(dev);
                            error = bus_add_device(dev);
                            error = dpm_sysfs_add(dev);
                            device_pm_add(dev);
                            kobject_uevent(&dev->kobj, KOBJ_ADD);
                            bus_probe_device(dev); /*探测新设备的驱动程序*/
                            	->进入 void bus_probe_device(struct device *dev)
                                ret = device_attach(dev);
                                	->进入 int device_attach(struct device *dev)
                                    if (dev->driver) { /*该设备已经匹配了驱动*/
                                        klist_node_attached(&dev->p->knode_driver)
                                        device_bind_driver(dev);
                                    } else { /*该驱动为匹配过驱动*/
                                        bus_for_each_drv(dev->bus, NULL, dev, __device_attach); /*为新加的这个设备轮询所有驱动,对每个驱动调用  __device_attach 与设备进行匹配*/
                                            ->进入 __device_attach(struct device_driver *drv, void *data)
                                            driver_match_device(drv, dev)
                                                ->进入 driver_match_device(struct device_driver *drv, struct device *dev)
                                                drv->bus->match(dev, drv); /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.match*/
                                            driver_probe_device(drv, dev);
                                                ->进入 driver_probe_device(struct device_driver *drv, struct device *dev)
                                                really_probe(dev, drv);
                                                    dev->driver = drv; /*在数据结构体上关联驱动到设备的 driver 字段*/
                                                    dev->bus->probe(dev); /*回调设备的 probe 函数*/
                                                    drv->probe(dev);
                                                        ->i2c_device_probe(struct device *dev) /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.probe*/
                                                            driver->probe(client, i2c_match_id(driver->id_table, client)); /*回调进入平台驱动过的 probe 函数,即 pcf8563_probe 函数*/
                                    }
                ...
        /*遍历 i2c 总线上的全部驱动,为每个驱动调用 __process_new_adapter 函数
        动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式*/
        bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
        	->进入 __process_new_adapter(struct device_driver *d, void *data)
            i2c_do_add_adapter(to_i2c_driver(d), data);
            	->进入 i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
                i2c_detect(adap, driver); /*i2c 检测,动态增加匹配到的设备*/
                    ->进入 i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
                    int adap_id = i2c_adapter_id(adapter);
                    address_list = driver->address_list;
                    if (!driver->detect || !address_list) /*驱动 driver 需要实现 .address_list .detect 才能动态添加驱动层 platform_device*/
                    temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); /*新增一个 i2c_client,主要是保存 i2c 信息*/
                    for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { /*轮询 address_list 数组,匹配所有地址*/
                        temp_client->addr = address_list[i];
                        err = i2c_detect_address(temp_client, driver);
                            ->进入 i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver)
                            err = i2c_check_addr_validity(addr); /*检测地址有效性*/
                            i2c_check_addr_busy(adapter, addr) /*检测地址是否冲突*/
                            i2c_default_probe(adapter, addr) /*与外接 i2c 设备通信一下看是否能正常通信,确保接了实际设备*/
                                ->进入 i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
                                if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
                                    /*特殊处理地址为 0x30-0x37 和 0x50-0x5f 而且支持 I2C_FUNC_SMBUS_QUICK 功能的设备*/
                                    i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_QUICK, NULL);
                                else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) /*检测 i2c 适配器是否支持读字节, am335x 的 i2c 适配器支持该功能*/
                                    i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy); /*i2c 读取一个字节*/
                                        ->进入 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr...)
                                        flags &= I2C_M_TEN | I2C_CLIENT_PEC;
                                        /*am335x 不支持 smbus 算法, 进入以下函数*/
                                        i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data); /*使用 i2c 协议模拟 smbus 协议收发*/
                                            ->进入 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr...)
                                            /*打包 i2c 消息 msg*/
                                            ...
                                            i2c_transfer(adapter, msg, num); /*i2c 发送消息 msg,进行一次读写,从而判断是否有外接 i2c 设备*/
                                ...
                            memset(&info, 0, sizeof(struct i2c_board_info)); /*初始化新增的 board_info*/
                            info.addr = addr;
                            err = driver->detect(temp_client, &info); /*回调到驱动的 .detect 函数*/
                            client = i2c_new_device(adapter, &info); /*如果回调之后的 info.type[0] 被赋值了,则新建一个 加驱动层 platform_device*/
                    }
                driver->attach_adapter(adap); /*i2c 驱动加载后的回调,内核以后会去掉这个回调, 一般不赋值这个回调接口*/

6.3.i2c_add_driver()

提供给驱动层调用, 注册一个驱动

i2c_add_driver(driver)
    i2c_register_driver(THIS_MODULE, driver)
        ->进入 i2c_register_driver(struct module *owner, struct i2c_driver *driver)
        driver->driver.bus = &i2c_bus_type; /*配置好 platform_driver 与 platform_device 匹配、检测的回调*/
            -> struct bus_type i2c_bus_type 定义
            struct bus_type i2c_bus_type = {
                .match		= i2c_device_match,
                .probe		= i2c_device_probe,
                ...
            }
        driver_register(&driver->driver); /*注册平台驱动*/
            ->进入 driver_register(struct device_driver *drv)
            ret = bus_add_driver(drv); /*添加驱动到平台总线上*/
                ->进入 bus_add_driver(struct device_driver *drv)
                error = driver_attach(drv); /*驱动检测*/
                    ->进入 driver_attach(struct device_driver *drv)
                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); /*轮询总线上的所有设备,对每个设备调用 __driver_attach 与驱动进行匹配*/
                        ->进入 __driver_attach(struct device *dev, void *data)
                        driver_match_device(drv, dev) /*匹配设备和驱动*/
                            ->进入 driver_match_device(struct device_driver *drv, struct device *dev)
                            drv->bus->match(dev, drv); /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.match*/
                        driver_probe_device(drv, dev); /*驱动的探测*/
                            ->进入 driver_probe_device(struct device_driver *drv, struct device *dev)
                            really_probe(dev, drv);
                                dev->driver = drv; /*在数据结构体上关联驱动到设备的 driver 字段*/
                                dev->bus->probe(dev); /*回调设备的 probe 函数*/
                                drv->probe(dev);
                                    -> i2c_device_probe(struct device *dev) /*回调进入驱动配置好的总线回调函数中 i2c_bus_type.probe*/
                                        driver->probe(client, i2c_match_id(driver->id_table, client)); /*回调进入平台驱动过的 probe 函数,即 pcf8563_probe 函数*/
    /*遍历 i2c 总线上的全部设备,为每设备调用 __process_new_driver 函数
    动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式*/
    i2c_for_each_dev(driver, __process_new_driver);
        ->进入 __process_new_driver(struct device *dev, void *data)
        if (dev->type != &i2c_adapter_type) /*如果不是 i2c 适配器类型,直接返回,这里就过滤掉了 board info 生成的 i2c 客户端(节点)类型的设备*/
            return 0;
        i2c_do_add_adapter(data, to_i2c_adapter(dev));
            ->进入 i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
            /*下面的操作跟 i2c_add_numbered_adapter 的一致*/
            ...

6.2代码中第71行以及6.3代码中第30行中提及到的动态添加驱动层 platform_device ,这是生成驱动层平台设备的第二种方式, 我修改了实时时钟pcf8563的驱动源码实现了这个功能, 驱动源码修改内容可以下载参考: https://download.csdn.net/download/bruno_mars/11034092

6.4.i2c_transfer()

提供给驱动层调用, 通过 i2c 协议发送消息包 i2c_msg

i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    	if (adap->algo->master_xfer) /*只有实现了 i2c 算法 algo 中的 master_xfer 才能正常使用*/
            i2c_lock_adapter(adap); /*锁*/
            ret = adap->algo->master_xfer(adap, msgs, num); /*回调到适配器层的发送函数 master_xfer*/
                /*回调进入适配器层, 适配器实现了 master_xfer 函数读写寄存器将数据包 msgs 发送出去*/
                ...
            i2c_unlock_adapter(adap); /*解锁*/

一个消息包 i2c_msg 由以下部分组成:

addr
从机的地址
flags
消息处理标志位
len
msg 附带的数据的长度
buf
msg 附带的数据指针

i2c 数据包结构体如下:

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

6.5.其他

其他i2c核心层函数请参考内核源码

你可能感兴趣的:(Linux,驱动)