I2C(三) linux3.4(内核分析)

[TOC]
## (一)总线流程
可以看下总线的匹配函数
```c
struct bus_type i2c_bus_type = {
        .name                = "i2c",
        .match                = i2c_device_match,
        .probe                = i2c_device_probe,
        .remove                = i2c_device_remove,
        .shutdown        = i2c_device_shutdown,
        .pm                = &i2c_device_pm_ops,
};
```
### bus.probe
这里插入一下`bus``probe`,简单的搜索`probe`,可以在`drivers\base\bus.c`搜索到一些东西,继续查看,可以看到有如下函数,也就是先执行总线的`probe`,再执行具体驱动的`probe`
```c
driver_probe_device
        > really_probe
                > dev->bus->probe(dev)
            > drv->probe(dev)
```
那么谁来调用这个`driver_probe_device`,搜索可以看到有以下函数调用,具体的`device_attach``driver_attach`就不深入了,水平还不够
```c
static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
        >driver_bind
                >driver_probe_device(drv, dev)
device_attach
        >__device_attach
                >driver_match_device 先匹配
                >driver_probe_device(drv, dev)
driver_attach
        >__driver_attach
                >driver_match_device 先匹配
                >driver_probe_device(drv, dev)
                
int driver_probe_device(struct device_driver *drv, struct device *dev)
        >really_probe(dev, drv);
        {
                dev->bus->probe(dev)
                drv->probe(dev)
        }
```
### match
这里匹配的是`dev `的母体结构`client`的成员`name``driver``id_table`
```c
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
        //先找到 dev 的母结构  i2c_client的地址
        struct i2c_client        *client = i2c_verify_client(dev);
    //通过 driver 找到 i2c_driver
    driver = to_i2c_driver(drv);
        if (driver->id_table)
                return i2c_match_id(driver->id_table, client) != NULL;
                                        > if (strcmp(client->name, id->name) == 0)
}
```
### i2c_device_probe
匹配之后,会调用这个函数,这个函数会这里会将`client` 绑定具体的`driver`,再调用实际驱动的`probe`,
```c
client->driver = driver;
driver->probe(client, i2c_match_id(driver->id_table, client));
```
```c
static int i2c_device_probe(struct device *dev)
{
        struct i2c_client        *client = i2c_verify_client(dev);
        struct i2c_driver        *driver;
        int status;
        driver = to_i2c_driver(dev->driver);
        if (!driver->probe || !driver->id_table)
                return -ENODEV;
    // 这里会将client 绑定具体的driver
        client->driver = driver;
        if (!device_can_wakeup(&client->dev))
                device_init_wakeup(&client->dev,
                                        client->flags & I2C_CLIENT_WAKE);
        dev_dbg(dev, "probe\n");
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
        if (status) {
                client->driver = NULL;
                i2c_set_clientdata(client, NULL);
        }
        return status;
}
```
## (二)client注册
这里的client,指的是能够挂接到总线上的设备,或者是说你强制认为总线上就有这个设备.具体的结构如下:
```c
struct i2c_client {
        unsigned short flags;                /* div., see below                */
        unsigned short addr;                /* chip address - NOTE: 7bit        */
                                        /* addresses are stored in the        */
                                        /* _LOWER_ 7 bits                */
        char name[I2C_NAME_SIZE];
        struct i2c_adapter *adapter;        /* the adapter we sit on        */
        struct i2c_driver *driver;        /* and our access routines        */
        struct device dev;                /* the device structure                */
        int irq;                        /* irq issued by device                */
        struct list_head detected;
};
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
```
- `struct i2c_adapter *adapter` 指的是挂载的具体的哪个总线
- `struct i2c_driver *driver`指的是我们用什么驱动,这里指的是自己的字符设备或者块设备等
- `struct device dev`这个结构比较关键,这个就是总线设备模型`dev---driver`部分中左侧`dev`部分
- 当我们挂载到bus上的时候,通过bus.probe就会将对应的`driver`附到`client.driver`上了
### 方式(一)静态加载
#### i2c_register_board_info
这种方式是编译到内核,注册信息
```c
i2c_register_board_info
```
我们一般使用如下方式
```c
//arch\arm\mach-s3c24xx\mach-mini2440.c
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
        {
                I2C_BOARD_INFO("24c08", 0x50),
                .platform_data = &at24c08,
        },
};
static void __init mini2440_init(void)
{
        i2c_register_board_info(0, mini2440_i2c_devs,
                                ARRAY_SIZE(mini2440_i2c_devs));
}
```
具体是怎么注册的?实际上是将具体的这个硬件信息先构造成`i2c_devinfo`结构,再放入到一个全局的链表中
```c
i2c_register_board_info(int busnum,
        struct i2c_board_info const *info, unsigned len)
{
        if (busnum >= __i2c_first_dynamic_bus_num)
                __i2c_first_dynamic_bus_num = busnum + 1;
        for (status = 0; len; len--, info++) {
                struct i2c_devinfo        *devinfo;
                devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
                devinfo->busnum = busnum;
                devinfo->board_info = *info;
                //这是一个全局的链表,也就是先分配一个i2c_devinfo.board_info 使用的就是单板传递的信息
                list_add_tail(&devinfo->list, &__i2c_board_list);   
        }
        return status;
}
```
这个全局的链表内容是这样的
```c
struct i2c_devinfo {
        struct list_head        list;
        int                        busnum;                                                        //链表上的序号
        struct i2c_board_info        board_info;
        {
                char                type[I2C_NAME_SIZE];                //设备名,
                unsigned short        flags;                                        //将来传递给client
                unsigned short        addr;                                        //设备地址
                void                *platform_data;                                
                struct dev_archdata        *archdata;                
                struct device_node *of_node;
                int                irq;
        };
};
```
#### i2c_scan_static_board_info
谁会来调用这个链表呢?也就是根据这个信息添加client到总线上,很显然,是我们在注册总线的时候,来去遍历这个信息,也就是函数`i2c_scan_static_board_info`
```c
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo  *devinfo;
    list_for_each_entry(devinfo, &__i2c_board_list, 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`来操作,提前透露一下,这个函数是构造`client`,加入到总线设备的dev链表中去
我们从` if (devinfo->busnum == adapter->nr`这里可以看出来适配器的选择,也就是
- `devinfo->busnum`也指的是总线的编号
- `adapter->nr `应该是适配器的编号,也就是总线的编号
#### i2c_new_device
这里我们根据`i2c_board_info >构造链表__i2c_board_list `来创建一个实际的`client`,然后将这个`client`的元素`dev`挂载到总线设备模型的dev上
```c
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client   *client;
    int         status;
    client = kzalloc(sizeof *client, GFP_KERNEL); 先分配结构体
        //  指定具体的adapt适配器
    client->adapter = adap;
        //   指定挂载在总线设备平台上的 platform_data ,挂载在总线平台上的是dev 而不是 client
    client->dev.platform_data = info->platform_data; 
        //    这个数据属于架构的数据
    if (info->archdata)
        client->dev.archdata = *info->archdata;
        //具体硬件信息
    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;
    strlcpy(client->name, info->type, sizeof(client->name));
        //地址合法性检测
    /* Check for address validity */
    status = i2c_check_client_addr_validity(client);
    if (status) {
        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
        goto out_err_silent;
    }
    /* Check for address business */  7位地址和10位地址判断
    status = i2c_check_addr_busy(adap, client->addr);
        
    if (status)
        goto out_err;
        
        //设置具体总线平台的类型,节点,设置名字
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;
        //kobject.name =%d-%04x   适配器.nr- client->addr(10位地址还要|0xa0000)
    /* For 10-bit clients, add an arbitrary offset to avoid collisions */
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
             client->addr | ((client->flags & I2C_CLIENT_TEN)
                     ? 0xa000 : 0));
    //注册client->dev
    status = device_register(&client->dev);
    if (status)
        goto out_err;
    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
        client->name, dev_name(&client->dev));
    return client;
}
```
- `client->dev.parent = &client->adapter->dev;` 这里将总线设备的这个元素只想来适配器的`dev`
#### i2c_register_adapter
那么谁来调用这个静态扫描的函数呢?很显然是在注册适配器的时候,也就是注册I2C控制器
```c
if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
```
#### 小结
单板信息最终会加入到这个链表,注册`adapt `的时候会扫描这个链表  生成`client`,将这个`client.dev`挂载到 总线平台设备的` dev`链表上,那么我们如何去找到这个client,在这里有一个宏 
```c
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
```
这里的`i2c_board_info.type==i2c_client.name` 用作总线设备的匹配
### 方式(二)指定设备
这个方式实际上就是跳过通过单板信息构造链表,直接调用`i2c_new_device`来构造`client`,加入到总线设备模型的`dev`链表中,`i2c_new_probed_device`则是先判断下设备地址是否存在再来创建,这个函数还能指定自定义的地址检测方式
```c
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
i2c_new_probed_device(struct i2c_adapter *adap,
                      struct i2c_board_info *info,
                      unsigned short const *addr_list,
                      int (*probe)(struct i2c_adapter *, unsigned short addr))
{
        if (!probe)
                probe = i2c_default_probe;
    
    if (i2c_check_addr_busy(adap, addr_list[i]))
        ...
    probe(adap, addr_list[i])
        return i2c_new_device(adap, info);
}
```
具体的实例代码如下
```c
static struct i2c_board_info at24cxx_info = {        
        I2C_BOARD_INFO("at24c08", 0x50),   //设置tyep 也就是后来赋值给client的name
    #define I2C_BOARD_INFO(dev_type, dev_addr) \
        .type = dev_type, .addr = (dev_addr)
};
static struct i2c_client *at24cxx_client;
static 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);
        
        return 0;
}
/// 检测地址
static struct i2c_client *at24cxx_client;
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };
static int at24cxx_dev_init(void)
{
        struct i2c_adapter *i2c_adap;
        struct i2c_board_info at24cxx_info;
        memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));        
        strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE);
        i2c_adap = i2c_get_adapter(0);
        at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);
        i2c_put_adapter(i2c_adap);
        if (at24cxx_client)
                return 0;
        else
                return -ENODEV;
}
```
### 方式(三)用户空间
直接在`shell`下操作文件
```c
# 创建
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
# 删除
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
```
搜索下`new_device`,实际最终调用函数`i2c_sysfs_new_device`,这个函数内部也是调用`i2c_new_device`
```c
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
```
### 方式(四)遍历适配器
前面三个都是指定适配器去挂接设备,那么假设不知道适配器,我们如何去遍历适配器,自动识别出在那个适配器上然后去挂接上`client`呢?这里使用`i2c_add_driver`
#### 注意
实际上我们的适配器也是挂载到设备平台`dev`链表中,通过`type`来区分
#### **简介** 
1. 使用`i2c_add_driver`注册用户驱动
2. 匹配已有的挂载`client`,调用`probe`
3. 遍历左侧的`dev`链表,挑选`adapt`,来识别驱动自身指定的`address_list`,如果识别到设备存在
4. 调用驱动的`detect`来做进一步检测,设置`i2c_board_info.type`,赋值`client`做匹配
5. 使用`i2c_new_device`来挂载这个`client`,这里会和`i2c_driver``id_table`比较,执行驱动的`probe`
    这里一定会匹配上的,因为client.name就是`i2c_board_info.type`也就是`id_table.name`
#### 代码注释(可以看下后面的设备驱动章节)
```c
i2c_add_driver
        i2c_register_driver
                a. at24cxx_driver放入i2c_bus_type的drv链表
                   并且从dev链表里取出能匹配的i2c_client并调用probe
                driver_register
                        
                
                b. 对于每一个适配器,调用__process_new_driver
                   对于每一个适配器,调用它的函数确定address_list里的设备是否存在
                   如果存在,再调用detect进一步确定、设置,然后i2c_new_device
                /* Walk the adapters that are already present */
                i2c_for_each_dev(driver, __process_new_driver);
            //下面这个函数是总线设备的通用函数 ,注释上的意思应该就是
            // 在某类设备总线上,从 start的dev开始遍历,执行以fn为函数的,data为参数的回调i2c_driver
            // 这里也就是对于  i2c_bus_type 上的dev 执行 __process_new_driver  参数是driver也就是 i2c_driver
            //int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *))
            >bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
                        //可以先看一下__process_new_driver ,按照之前的注释 data是i2c_driver ,也就是 参数是 __process_new_driver
                        __process_new_driver
                                //这里的dev 是指的适配器,实际上适配器和设备都是挂载在一个链表上的,根据type分辨
                                // 确保遍历的dev 是适配器类型
                if (dev->type != &i2c_adapter_type)
                    return 0;
                                i2c_do_add_adapter
                                        /* Detect supported devices on that bus, and instantiate them */
                                        i2c_detect(adap, driver);
                                                for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                                                        err = i2c_detect_address(temp_client, driver);
                                                                                /* 判断这个设备是否存在:简单的发出S信号确定有ACK */
                                                                                if (!i2c_default_probe(adapter, addr))
                                                                                        return 0;
                                                                                
                                                                                memset(&info, 0, sizeof(struct i2c_board_info));
                                                                                info.addr = addr;        
                                                                                
                                                    //这里是我们自己的检测函数
                                                                                // 设置info.type
                                                                                err = driver->detect(temp_client, &info);
                                        
                                                                                i2c_new_device
```
#### 代码举例
```c
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,   /* 这些设备的地址 */
};
//用作 dev 和 driver 的匹配,这里如果能识别,肯定赋值个 client.name 
static const struct i2c_device_id at24cxx_id_table[] = {
        { "at24c08", 0 },
        {}
};
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };
    
去"class表示的这一类"I2C适配器,用"detect函数"来确定能否找到"address_list里的设备",
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe
```
## (三)适配器
> drivers\i2c\busses\i2c-s3c2410.c
注册适配器最终会调用`i2c_driver.detect`来设置具体的匹配的参数`i2c_client.type`,,挂载`client`到链表
然后调用驱动`i2c_driver.probe`注册字符设备驱动等
### 引入
驱动首先从入口开始看起,可以看到是`platform`框架,也就是驱动入口执行函数是`driver.probe`
```c
static int __init i2c_adap_s3c_init(void)
{
        return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);
static struct platform_driver s3c24xx_i2c_driver = {
        .probe                = s3c24xx_i2c_probe,
        .remove                = s3c24xx_i2c_remove,
        .id_table        = s3c24xx_driver_ids,
        .driver                = {
                .owner        = THIS_MODULE,
                .name        = "s3c-i2c",
                .pm        = S3C24XX_DEV_PM_OPS,
                .of_match_table = s3c24xx_i2c_match,
        },
};
static struct platform_device_id s3c24xx_driver_ids[] = {
        {
                .name                = "s3c2410-i2c",
                .driver_data        = TYPE_S3C2410,
        }, {
                .name                = "s3c2440-i2c",
                .driver_data        = TYPE_S3C2440,
        }, { },
};
```
### s3c24xx_i2c_probe
1. 设置具体的`adapt`结构
2. 设置中断
3. 使用`i2c_add_numbered_adapter`或者`i2c_add_adapter`来注册这个`adapt`
4. 使用`of_i2c_register_devices`会调用`i2c_new_device`添加`client`到链表
```c
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
        struct s3c24xx_i2c *i2c;
        struct s3c2410_platform_i2c *pdata = NULL;
        struct resource *res;
        int ret;
        if (!pdev->dev.of_node) {
                pdata = pdev->dev.platform_data;
                if (!pdata) {
                        dev_err(&pdev->dev, "no platform data\n");
                        return -EINVAL;
                }
        }
        i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
        i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (pdata)
                memcpy(i2c->pdata, pdata, sizeof(*pdata));
        else
                s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
        
        //设置adapt 的参数
        strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
        i2c->adap.owner   = THIS_MODULE;
        // adap.algo 这是具体的硬件传输的函数 
        i2c->adap.algo    = &s3c24xx_i2c_algorithm;
        //数据传输重试次数
        i2c->adap.retries = 2;                                                                                        
        i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        i2c->tx_setup     = 50;
        spin_lock_init(&i2c->lock);
        
        //初始化等待队列,这个在后续的休眠中使用,触发中断后休眠
        init_waitqueue_head(&i2c->wait);
        /* find the clock and enable it */
        i2c->dev = &pdev->dev;
        i2c->clk = clk_get(&pdev->dev, "i2c");
        clk_enable(i2c->clk);
        /* map the registers */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        i2c->ioarea = request_mem_region(res->start, resource_size(res),pdev->name);
        i2c->regs = ioremap(res->start, resource_size(res));
        /* setup info block for the i2c core */
        i2c->adap.algo_data = i2c;
        i2c->adap.dev.parent = &pdev->dev;
        // 初始化I2c 寄存器 gpio复用
        /* initialise the i2c controller */
        ret = s3c24xx_i2c_init(i2c);
        // 设置中断函数,真正的数据传输
        i2c->irq = ret = platform_get_irq(pdev, 0);
        ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,dev_name(&pdev->dev), i2c);
        ret = s3c24xx_i2c_register_cpufreq(i2c);
        
        // 这里使用指定 adapt 编号的方式,也可以使用 i2c_add_adapter 来注册这个adapt
        
        /* Note, previous versions of the driver used i2c_add_adapter()
         * to add the bus at any number. We now pass the bus number via
         * the platform data, so if unset it will now default to always
         * being bus 0.
         */
        i2c->adap.nr = i2c->pdata->bus_num;
        i2c->adap.dev.of_node = pdev->dev.of_node;
        ret = i2c_add_numbered_adapter(&i2c->adap);
        //这个会挂接client 到链表
        of_i2c_register_devices(&i2c->adap);
                > i2c_new_device  
                
                
                
        platform_set_drvdata(pdev, i2c);
        
        pm_runtime_enable(&pdev->dev);
        pm_runtime_enable(&i2c->adap.dev);
        dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
        clk_disable(i2c->clk);
        return 0;
}
```
### 注册适配器
这里使用`i2c_add_adapter`或者`i2c_add_numbered_adapter`来注册适配器,最终都是调用`i2c_register_adapter(adap)`来注册适配器的
#### i2c_register_adapter
这里的具体看代码注释,最终会调用`__process_new_adapter`,应该是要完成实际的驱动注册
```c
static int i2c_register_adapter(struct i2c_adapter *adap)
{
        rt_mutex_init(&adap->bus_lock);
        mutex_init(&adap->userspace_clients_lock);
        INIT_LIST_HEAD(&adap->userspace_clients);
        //设置 adapt的dev.type,后续能够根据这个和client区别
        // 注册adap->dev 到总线平台的左侧
        dev_set_name(&adap->dev, "i2c-%d", adap->nr);
        adap->dev.bus = &i2c_bus_type;
        adap->dev.type = &i2c_adapter_type;
        res = device_register(&adap->dev);
        // 在系统初始化的时候,我们手动添加了一些外设信息,期望构造client加入到dev链表
        // 如果没有合适的adapt,我们并不能将其构造为client加入,而是保留在链表中
        // 我们新注册了一个adapt,理所当然需要使用这个新的adapt去尝试构造一个client
        /* create pre-declared device nodes */
        if (adap->nr < __i2c_first_dynamic_bus_num)
                i2c_scan_static_board_info(adap);
        // 针对链表上的所有 driver,调用adapt的硬件操作,执行__process_new_adapter
        // 执行__process_new_adapter 应该是要调用驱动driver 的detect 来创建字符设备等驱动
        bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
}
```
#### __process_new_adapter
- 这个函数是我们遍历总线的driver,执行这个函数,也就是遍历我们右侧的的`i2c_driver`
- 这个函数从名字看就和`__process_new_driver`很像,从`bus_for_each_drv`的注释看出来`data`是作为回调的参数,也就是说最终是`i2c_do_add_adapter(to_i2c_driver(d), adapt);`,可以看到`i2c_do_add_adapter`的原型的第二个参数确实是`adapt`
```c
/**
 * bus_for_each_drv - driver iterator
 * @bus: bus we're dealing with.
 * @start: driver to start iterating on.
 * @data: data to pass to the callback.
 * @fn: function to call for each driver.
 *
*/
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
                     void *data, int (*fn)(struct device_driver *, void *))
    
    
static int __process_new_adapter(struct device_driver *d, void *data)
{
        return i2c_do_add_adapter(to_i2c_driver(d), data);
}
//上面的data也就是bus_for_each_drv的data 也就是最开始要注册的adapt
static int i2c_do_add_adapter(struct i2c_driver *driver,
                              struct i2c_adapter *adap)
```
#### i2c_do_add_adapter
这里函数的关键是`i2c_detect`,3.4内核的`driver->attach_adapter`可以忽略了
```c
static int i2c_do_add_adapter(struct i2c_driver *driver,
                              struct i2c_adapter *adap)
{
        /* Detect supported devices on that bus, and instantiate them */
        i2c_detect(adap, driver);
        // 下面这个driver->attach_adapter 是2.6上的匹配到硬件地址后,
        // 用来创建字符设备驱动等操作的函数,3.4内核上使用上面的 i2c_detect 来实现这个功能
        // 一般不需要使用,可以看到结构定义是 __deprecated ,也就是不推荐的了
        
        /* Let legacy drivers scan this bus for matching devices */
        if (driver->attach_adapter) {
                dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
                         driver->driver.name);
                dev_warn(&adap->dev, "Please use another way to instantiate "
                         "your i2c_client\n");
                /* We ignore the return code; if it fails, too bad */
                driver->attach_adapter(adap);
        }
        return 0;
}
```
#### i2c_detect
这里先判断下`adapt``driver`类型,然后构造一个临时的`client`,地址是`driver->address_list`中的列表,依次尝试`i2c_detect_address`来检测
```c
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);
        address_list = driver->address_list;
        if (!driver->detect || !address_list)
                return 0;
        /* Stop here if the classes do not match */
        if (!(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;
        // 创建一个临时的 client,使用 i2c_detect_address来尝试操作
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                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;
}
```
#### i2c_detect_address
1. 这里会使用 `i2c_smbus_xfer `来实际通信确保硬件存在
2. 调用最后的 用户驱动 `i2c_driver.detect`
3. 成功后使用`i2c_new_device`  安装`client``dev `链表,`i2c_new_device`  在上述章节回顾
```c
static int i2c_detect_address(struct i2c_client *temp_client,
                              struct i2c_driver *driver)
{
        struct i2c_board_info info;
        struct i2c_adapter *adapter = temp_client->adapter;
        int addr = temp_client->addr;
        int err;
        /* Make sure the address is valid */
        err = i2c_check_addr_validity(addr);
        if (err) {
                dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
                         addr);
                return err;
        }
        /* Skip if already in use */
        if (i2c_check_addr_busy(adapter, addr))
                return 0;
        // 这里会使用 i2c_smbus_xfer 来实际通信确保硬件存在
        /* Make sure there is something at this address */
        if (!i2c_default_probe(adapter, addr))
                return 0;
        // 调用最后的 用户驱动 i2c_driver.detect
        /* Finally call the custom detection function */
        memset(&info, 0, sizeof(struct i2c_board_info));
        info.addr = addr;
        err = driver->detect(temp_client, &info);
        if (err) {
                /* -ENODEV is returned if the detection fails. We catch it
                   here as this isn't an error. */
                return err == -ENODEV ? 0 : err;
        }
        
        // i2c_new_device  安装client 到dev 链表
        /* Consistency check */
        if (info.type[0] == '\0') {
                dev_err(&adapter->dev, "%s detection function provided "
                        "no name for 0x%x\n", driver->driver.name,
                        addr);
        } else {
                struct i2c_client *client;
                /* Detection succeeded, instantiate the device */
                dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
                        info.type, info.addr);
                client = i2c_new_device(adapter, &info);
                if (client)
                        list_add_tail(&client->detected, &driver->clients);
                else
                        dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
                                info.type, info.addr);
        }
        return 0;
}
```
#### i2c_driver.detect
接下去就是要分析用户的设备驱动`i2c_driver.detect`,**设置匹配参数**
1. 用来判断那些可能设备地址相同的不同类型的设备.
2. 设置具体的`i2c_board_info.type`,这个参数会在`i2c_new_device`赋值给`client`,用作`i2c`总线平台的匹配参数
3. 这会触发`i2c_driver``probe`
### 接下去执行驱动probe
这里就是执行字符设备驱动的注册等了
### 硬件操作
硬件操作最后归结到接口`master_xfer`,I2C的传输到最后可以整理为
1. 开始信号
2. 结束信号
3. ACK/NAK
4. 数据传输1字节,读写是根据起始信号决定
在官方的驱动中,使用中断来传输,传输中使用休眠唤醒
```c
//休眠
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
//完成后唤醒
wake_up(&i2c->wait);
// probe 时候申请中断
request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);
```
## 设备驱动
## (四)设备驱动i2c_driver
> drivers\misc\eeprom\eeprom.c
>
> drivers\misc\eeprom\at24.c
>
> misc 是杂项的意思
### 引入
从入口看起
```c
static int __init at24_init(void)
{
        return i2c_add_driver(&at24_driver);
}
module_init(at24_init);
```
### 注册设备驱动
#### i2c_add_driver
```c
/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \
        i2c_register_driver(THIS_MODULE, driver)
```
#### i2c_register_driver
1. 注册驱动程序,智力应该就会去执行`probe `函数了
2. 遍历这个驱动`driver`,执行`__process_new_driver`
```c
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
        int res;
        /* Can't register until after driver model init */
        if (unlikely(WARN_ON(!i2c_bus_type.p)))
                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;
        
        // 注册驱动程序,智力应该就会去执行probe 函数了
        
        /* When registration returns, the driver core
         * will have called probe() for all matching-but-unbound devices.
         */
        res = driver_register(&driver->driver);
        // 遍历这个驱动,执行__process_new_driver
        INIT_LIST_HEAD(&driver->clients);
        /* Walk the adapters that are already present */
        i2c_for_each_dev(driver, __process_new_driver);
        return 0;
}
```
#### probe
综合来看这两个例子,`probe`里面就是创建字符设备驱动类似的操作,`at24`里面的比较复杂,但都是一些具体的操作
#### __process_new_driver
这个函数最终调用了`i2c_do_add_adapter`,也就是和注册适配器后的操作一致,挂接client,绑定client,driver,adapt
```c
static int __process_new_driver(struct device *dev, void *data)
{
        if (dev->type != &i2c_adapter_type)
                return 0;
        return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
```
这个函数理论上需要驱动实现`detect`,但是我在`at24.c`里面没有找到,在`eeprom.c`里面是有的,所以这个驱动应该是先有设备驱动和适配器,然后去手动构造`client`,这样就是另外的路线了
#### i2c_do_add_adapter
#### i2c_detect
#### i2c_detect_address
#### i2c_driver.detect
#### 接下去执行驱动probe
这几个函数都和注册适配器的是一样的,回去看上面的就可以了
## 构造设备驱动
### 方式(一) APP>驱动
> 参考内核文档 Documentation\i2c\smbus-protocol
这里就是在`i2c_driver``probe`中注册我们真实的驱动程序,比如构造字符设备驱动程序,提供读写的接口即可
`EEPROM`的普通读写,可以使用以下简单的函数,参考`Documentation\i2c\smbus-protocol`,`smbus``i2c`的一个子集
```c
//读
i2c_smbus_read_byte_data(at24cxx_client, addr)
//写
i2c_smbus_write_byte_data(at24cxx_client, addr, data)
```
### 方式(二)使用i2c-dev
使用通用的一个驱动程序`i2c-dev`来控制总线,去读写设备.其实我们可以看到内核源码目录下有个`i2c-dev.c`,就是这个文件,它类似一个通用的驱动.它实现了一个字符设备驱动,次设备号表示总线编号
内核配置
```
Device Drivers
         I2C support
                <*>   I2C device interface
```
```c
//i2c_dev->adap->nr 指定的adapt
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
        struct i2c_dev *i2c_dev;
        list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
                if (i2c_dev->adap->nr == index)
                        goto found;
        }
        i2c_dev = NULL;
        return i2c_dev;
}
static const struct file_operations i2cdev_fops = {
        .owner                = THIS_MODULE,
        .llseek                = no_llseek,
        .read                = i2cdev_read,
        .write                = i2cdev_write,
        .unlocked_ioctl        = i2cdev_ioctl,
        .open                = i2cdev_open,
        .release        = i2cdev_release,
};
```
可以看下源码,实际上最终也是调用类似的函数,注意这里面的`i2cdev_read/i2cdev_write`只是单纯时序上的读写,我们操作`EE`的读操作实际上是需要先读再写地址的,所以不能用这个接口,应该用`i2cdev_ioctl`
```c
i2c_get_functionality
i2c_smbus_xfer
```
这里可以参考
> Linux设备驱动开发详解第2版-宋宝华.pdf   > 15.4.3 Linux 的 i2c-dev.c 文件分析
## 关于设备驱动detect
我们在分析注册设备驱动以及注册适配器驱动的时候,最终都会调用`i2c_driver.detect`,这个函数会设置具体的`i2c_board_info.type`,这个参数会在`i2c_new_device`赋值给`client`,用作`i2c`总线平台的匹配参数这会触发`i2c_driver``probe`.
但是我们在刚开始做的设置client的前三种方法,`i2c_driver`并没有设置`detect`,也就是说在函数中不会执行到这个过程,我们从公共的入口`i2c_detect`来看下,加入调试打印看看实际有没有执行
```c
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);
        address_list = driver->address_list;
        if (!driver->detect || !address_list)
    {
        // 在这里加入打印,看看实际驱动的detect 有没有默认值
        printk("no detect\n");
        return 0;
    }
    else
    {
        //打印实际的detect
        printk("detect is %p \n",driver->detect);
    }
        /* Stop here if the classes do not match */
        if (!(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;
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                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;
}
```
测试下代码`1th`,运行结果如下
```shell
/mnt/iic/1th # insmod at24cxx_drv.ko
no detect
/mnt/iic/1th # insmod at24cxx_dev.ko
/home/book/stu/iic/1th/at24cxx_drv.c at24cxx_probe 13
------------------------------------------------------------
/mnt/iic/1th # insmod  at24cxx_dev.ko
/mnt/iic/1th # insmod at24cxx_drv.ko
/home/book/stu/iic/1th/at24cxx_drv.c at24cxx_probe 13
no detect
```
可以看出来,不论是先加载`dev`还是先加载`driver`,都不会有`detect`,那为啥能正常工作执行`probe`呢?流程应该是这样的:
- 我们的`i2c_detect`实际上只是将`client`挂接到`dev`链表上,`i2c_client`上的`driver`驱动的依附是在`bus.probe=i2c_device_probe`中实现的
- `dev``driver`匹配上就会执行驱动的probe,也就是执行打印
所以:
1. 先加载`drv`时,运行到`i2c_detect`时直接打印`no detect`后退出,然后加载`dev`后匹配执行驱动的`probe`
2. 先加载`dev`时,没有匹配不动作,加载`drv`时,先匹配上了执行驱动的`probe`,再去执行`i2c_detect`,打印`no detect`
**补充:** 
1. 可以看到`i2c_detect`最终目的是为了挂接`client`,而我们手动创建`client`使用了`i2c_new_device`,跳过了这个步骤,所以也能运行.
2. 这里要区分两个步骤
    - 挂接`client``dev` 链表,使用`i2c_new_device`
    - 匹配`client``driver`,使用的是`bus.probe`
3. 也就是说如果我们不使用`i2c_new_device`来手动创建设备挂接到`dev`链表,就需要`detect`来辅助内核挂接`client`挂接
## 系统信息查看
我们可以通过查看文件信息来查看具体的`i2c`设备
1. 可以在`/sys/bus/i2c/`下查看设备文件,这里看出来适配器驱动与`client`都属于`device`,这里的`50`是设备地址.`0`是总线序号
    ```shell
    /mnt/iic/1th # ls /sys/bus/i2c/
    devices            drivers_autoprobe  uevent
    drivers            drivers_probe
    /mnt/iic/1th # ls /sys/bus/i2c/devices/
    0-0050  i2c-0
    /mnt/iic/1th # ls /sys/bus/i2c/drivers/
    100ask  at24    dummy
    ```
2. 查看已有的`i2c`总线
    ```shell
    #ls  /sys/class/i2c-adapter/
    i2c-0
    ```
3. 创建设备节点的`new_device``deleted_device`位置
    ```shell
    #ls  /sys/class/i2c-adapter/i2c-0/
    delete_device  name           power          uevent
    device         new_device     subsystem
    ```
4. 这个文件夹实际的链接` /sys/devices/platform/s3c2440-i2c/i2c-0`
    ```shell
    /sys/class/i2c-adapter/i2c-0   这是一个链接文件  /sys/devices/platform/s3c2440-i2c/i2c-0
    ```
5. 查看我们的`client`
    ```shell
    /sys/devices/platform # ls
    alarmtimer        s3c2440-nand      s3c24xx_led.1     soc-audio
    power             s3c2440-uart.0    s3c24xx_led.2     uevent
    s3c2410-lcd       s3c2440-uart.1    s3c24xx_led.3     wm8976-codec
    s3c2410-ohci      s3c2440-uart.2    s3c24xx_wm8976.0
    s3c2410-wdt       s3c24xx-iis       samsung-audio
    s3c2440-i2c       s3c24xx_led.0     snd-soc-dummy
    
    /sys/devices/platform/s3c2440-i2c # ls
    driver     i2c-0      modalias   power      subsystem  uevent
    
    /sys/devices/platform/s3c2440-i2c/i2c-0 # ls
    0-0051         device         new_device     subsystem
    delete_device  name           power          uevent
    
    
    这里的 0-0051 实际上就是我们挂载的clinet设备了
    ```
## 内核配置
如果要使用`i2c_dev.c`这个通用的驱动,可以查看同目录下的`Makefile`,需要配置`obj-$(CONFIG_I2C_CHARDEV)        += i2c-dev.o`
```makefile
────────────────────────────────────────────────────── Search Results ──────────────────────────────────────────────────────┐
  │ Symbol: I2C_CHARDEV [=m]                                                                                                   │
  │ Type  : tristate                                                                                                           │
  │ Prompt: I2C device interface                                                                                               │
  │   Defined at drivers/i2c/Kconfig:39                                                                                        │
  │   Depends on: I2C [=y]                                                                                                     │
  │   Location:                                                                                                                │
  │     -> Device Drivers                                                                                                      │
  │       -> I2C support (I2C [=y])  
```
关于适配器驱动,f看下`drivers\i2c\busses\Makefile`
```makefile
obj-$(CONFIG_I2C_S3C2410)        += i2c-s3c2410.o
```
也就是找到配置,去除这个
```shell
│ Symbol: I2C_S3C2410 [=y]                                                                                                   │
  │ Type  : tristate                                                                                                           │
  │ Prompt: S3C2410 I2C Driver                                                                                                 │
  │   Defined at drivers/i2c/busses/Kconfig:601                                                                                │
  │   Depends on: I2C [=y] && HAVE_S3C2410_I2C [=y]                                                                            │
  │   Location:                                                                                                                │
  │     -> Device Drivers                                                                                                      │
  │       -> I2C support (I2C [=y])                                                                                            │
  │         -> I2C Hardware Bus support  
```
立即登录, 发表评论.
没有帐号? 立即注册