• Linux Driver : i2c-gpio


    背景

    在学习高通平台的有关知识,看到一篇博客中介绍了GPIO模拟I2C设备,觉得挺有意思的。

    跟了一下代码,发现这种模拟方式已经有了一套内核框架。刚好,学习这个驱动有助于加深理解:

    1、i2c 子系统框架:

    • i2c_adapter对象实现了一组通过一个i2c控制器发送消息的所有信息,包括时序,地址等等, 即封装了i2c控制器的"控制信息"。它被i2c主机驱动创建,通过clien域和i2c_client和i2c_driver相连, 这样设备端驱动就可以通过其中的方法以及i2c物理控制器来和一个i2c总线的物理设备进行交互
    • i2c_algorithm描述一个i2c主机的发送时序的信息,该类的对象algo是i2c_adapter的一个域,其中的master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调,以完成i2c的读写。
    • i2c_client描述一个挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象,与i2c_driver对象匹配成功后通过detected和i2c_driver以及i2c_adapter相连,在控制器驱动与控制器设备匹配成功后被控制器驱动通过i2c_new_device()创建。
    • i2c_driver描述一个挂接在硬件i2c总线上的设备的驱动方法,即i2c设备的驱动对象,通过i2c_bus_type和设备信息i2c_client匹配,匹配成功后通过clients和i2c_client对象以及i2c_adapter对象相连
    • i2c_msg描述一个在设备端和主机端之间进行流动的数据,在设备驱动中打包并通过i2c_transfer()发送。相当于skbuf之于网络设备,urb之于USB设备。

    2、gpio子系统api的使用

    3、Linux 平台设备驱动

    内核:4.9

    介绍

    drivers/i2c/busses下包含各种I2C总线驱动,其中就包括了使用GPIO模拟I2C总线的驱动i2c-gpio.c。

    驱动分析

    i2c-gpio.c实现了gpio模拟I2C总线的驱动。总线也是个设备,在这里将总线当作平台设备处理,那驱动当然是平台设备驱动。

    注册与注销

    没有什么好说的,它的初始化和注销函数就是注册和注销一个平台设备驱动。

    static int __init i2c_gpio_init(void)  
    {  
        int ret;  
    
        ret = platform_driver_register(&i2c_gpio_driver);  
        if (ret)  
            printk(KERN_ERR "i2c-gpio: probe failed: %d
    ", ret);  
    
        return ret;  
    }  
    subsys_initcall(i2c_gpio_init);
    
    static void __exit i2c_gpio_exit(void)  
    {  
        platform_driver_unregister(&i2c_gpio_driver);  
    }  
    module_exit(i2c_gpio_exit);
    

    platform_driver

    直接看它的platform_driver结构i2c_gpio_driver

    #if defined(CONFIG_OF)
    static const struct of_device_id i2c_gpio_dt_ids[] = {
        { .compatible = "i2c-gpio", },
        { /* sentinel */ }
    };
    
    MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
    #endif
    
    static struct platform_driver i2c_gpio_driver = {
        .driver     = {
            .name   = "i2c-gpio",
            .of_match_table = of_match_ptr(i2c_gpio_dt_ids),
        },
        .probe      = i2c_gpio_probe,
        .remove     = i2c_gpio_remove,
    };
    

    平台驱动设备放在arch/arm/mach-xxxx/board-xxx.c中,例如:

     static struct i2c_gpio_platform_data i2c_gpio_adapter_data = {
         .sda_pin = PINID_GPMI_D05,
         .scl_pin = PINID_GPMI_D04,
         .udelay = 5, //100KHz
         .timeout = 100,
         .sda_is_open_drain = 1,
         .scl_is_open_drain = 1,
     };   
       
     static struct platform_device i2c_gpio = {
         .name = "i2c-gpio",
         .id = 0,
         .dev = {
             .platform_data = &i2c_gpio_adapter_data,
             .release = mxs_nop_release,
             },
     };
    

    在这里 struct platform_device结构中的 name字段要和 struct platform_driver中 driver字段中 name字段要相同,因为平台总线就是通过这个来判断设备和驱动是否匹配的。

    注意这里的 id将它赋值了 0,后面会提到这个id是用来软件上的一个总线号(nr)。

    i2c_gpio_adapter_data

    platform_device这个结构里面还包含一个最重要的数据 i2c_gpio_adapter_data,它的原型为是struct i2c_gpio_platform_data类型:

    定义在 include/linux/i2c-gpio.h中。

    /**
     * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
     * @sda_pin: GPIO pin ID to use for SDA
     * @scl_pin: GPIO pin ID to use for SCL
     * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
     * @timeout: clock stretching timeout in jiffies. If the slave keeps
     *  SCL low for longer than this, the transfer will time out.
     * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
     *  isn't actively driven high when setting the output value high.
     *  gpio_get_value() must return the actual pin state even if the
     *  pin is configured as an output.
     * @scl_is_open_drain: SCL is set up as open drain. Same requirements
     *  as for sda_is_open_drain apply.
     * @scl_is_output_only: SCL output drivers cannot be turned off.
     */
    struct i2c_gpio_platform_data {
        unsigned int    sda_pin;
        unsigned int    scl_pin;
        
        int     udelay;
        int     timeout;
        unsigned int    sda_is_open_drain:1;
        unsigned int    scl_is_open_drain:1;
        unsigned int    scl_is_output_only:1;
    };
    

    这个结构体主要描述gpio模拟i2c总线时的一些必要信息:

    • sda_pin和scl_pin表示使用哪两个IO管脚来模拟I2C总线
    • udelay和timeout分别为它的时钟频率和超时时间
    • sda_is_open_drain和scl_is_open_drain表示sda、scl这两个管脚是否是开漏(opendrain)电路,如果是则为1
    • scl_is_output_only表示scl这个管脚是否只是作为输出,如果是设置为1。

    probe

    回到驱动中,看最重要的i2c_gpio_probe。

    static int i2c_gpio_probe(struct platform_device *pdev)
    {
        struct i2c_gpio_private_data *priv;
        // 
        struct i2c_gpio_platform_data *pdata;
        struct i2c_algo_bit_data *bit_data;
        // i2c适配器
        struct i2c_adapter *adap;
        unsigned int sda_pin, scl_pin;
        int ret;
    
        /* First get the GPIO pins; if it fails, we'll defer the probe. */
        if (pdev->dev.of_node) {
            ret = of_i2c_gpio_get_pins(pdev->dev.of_node,
                           &sda_pin, &scl_pin);
            if (ret)
                return ret;
        } else {
            if (!dev_get_platdata(&pdev->dev))
                return -ENXIO;
            pdata = dev_get_platdata(&pdev->dev);
            sda_pin = pdata->sda_pin;
            scl_pin = pdata->scl_pin;
        }
    
        ret = devm_gpio_request(&pdev->dev, sda_pin, "sda");
        if (ret) {
            if (ret == -EINVAL)
                ret = -EPROBE_DEFER;    /* Try again later */
            return ret;
        }
        ret = devm_gpio_request(&pdev->dev, scl_pin, "scl");
        if (ret) {
            if (ret == -EINVAL)
                ret = -EPROBE_DEFER;    /* Try again later */
            return ret;
        }
    
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
            return -ENOMEM;
        adap = &priv->adap;
        bit_data = &priv->bit_data;
        pdata = &priv->pdata;
    
        if (pdev->dev.of_node) {
            pdata->sda_pin = sda_pin;
            pdata->scl_pin = scl_pin;
            of_i2c_gpio_get_props(pdev->dev.of_node, pdata);
        } else {
            memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata));
        }
    
        if (pdata->sda_is_open_drain) {
            gpio_direction_output(pdata->sda_pin, 1);
            bit_data->setsda = i2c_gpio_setsda_val;
        } else {
            gpio_direction_input(pdata->sda_pin);
            bit_data->setsda = i2c_gpio_setsda_dir;
        }
    
        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
            gpio_direction_output(pdata->scl_pin, 1);
            bit_data->setscl = i2c_gpio_setscl_val;
        } else {
            gpio_direction_input(pdata->scl_pin);
            bit_data->setscl = i2c_gpio_setscl_dir;
        }
    
        if (!pdata->scl_is_output_only)
            bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;
    
        if (pdata->udelay)
            bit_data->udelay = pdata->udelay;
        else if (pdata->scl_is_output_only)
            bit_data->udelay = 50;          /* 10 kHz */
        else
            bit_data->udelay = 5;           /* 100 kHz */
    
        if (pdata->timeout)
            bit_data->timeout = pdata->timeout;
        else
            bit_data->timeout = HZ / 10;        /* 100 ms */
    
        bit_data->data = pdata;
    
        adap->owner = THIS_MODULE;
        if (pdev->dev.of_node)
            strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
        else
            snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
    
        adap->algo_data = bit_data;
        adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        adap->dev.parent = &pdev->dev;
        adap->dev.of_node = pdev->dev.of_node;
    
        adap->nr = pdev->id;
        ret = i2c_bit_add_numbered_bus(adap);
        if (ret)
            return ret;
    
        platform_set_drvdata(pdev, priv);
    
        dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)
    ",
             pdata->sda_pin, pdata->scl_pin,
             pdata->scl_is_output_only
             ? ", no clock stretching" : "");
    
        return 0;
    }
    

    获取platform数据

    1、pdata = dev_get_platdata(&pdev->dev);正是我们在平台设备结构中定义的数据:我们能够从其中获取到gpio模拟i2c总线时的一些必要信息。

    申请有关的资源

    接下来使用gpio_request去申请这个两个GPIO管脚,申请的目的是为了防止管脚冲突。

        ret = devm_gpio_request(&pdev->dev, sda_pin, "sda");
        if (ret) {
            if (ret == -EINVAL)
                ret = -EPROBE_DEFER;    /* Try again later */
            return ret;
        }
    
        ret = devm_gpio_request(&pdev->dev, scl_pin, "scl");
        if (ret) {
            if (ret == -EINVAL)
                ret = -EPROBE_DEFER;    /* Try again later */
            return ret;
        }
    

    2、注意到这里使用kzalloc申请了一个struct i2c_gpio_private_data对象

    static int i2c_gpio_probe(struct platform_device *pdev)
    {
        struct i2c_gpio_private_data *priv;
        struct i2c_gpio_platform_data *pdata;
        struct i2c_algo_bit_data *bit_data;
        //
        unsigned int sda_pin, scl_pin;
        int ret;
    
        // ...
        
        // 申请 i2c_gpio_private_data,其中包含了 i2c_adapter i2c_algo_bit_data 这两个成员
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        adap = &priv->adap;
        bit_data = &priv->bit_data;
        pdata = &priv->pdata;
    

    主要是为了struct i2c_adapterstruct i2c_algo_bit_data这2个成员。

    // drivers/i2c/busses/i2c-gpio.c
    
    struct i2c_gpio_private_data {
        struct i2c_adapter adap;
        struct i2c_algo_bit_data bit_data;
        struct i2c_gpio_platform_data pdata;
    };
    
    • struct i2c_adapter:在I2C子系统中,I2C适配器使用结构struct i2c_adapter描述,代表一条实际的I2C总线。

    定义在include/linux/i2c.h

    struct i2c_adapter {  
        struct module *owner;  
        unsigned int id;  
        unsigned int class;       /* classes to allow probing for */  
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */  
        void *algo_data;  
      
        /* data fields that are valid for all devices   */  
        u8 level;           /* nesting level for lockdep */  
        struct mutex bus_lock;  
      
        int timeout;            /* in jiffies */  
        int retries;  
        struct device dev;      /* the adapter device */  
      
        int nr;  
        char name[48];  
        struct completion dev_released;  
    };  
    
    • struct i2c_algo_bit_data:用来定义对I2C-引脚的操作方式

    定义在include/linux/i2c-algo-bit.h中

    struct i2c_algo_bit_data {  
        void *data;     /* private data for lowlevel routines */  
        void (*setsda) (void *data, int state);  
        void (*setscl) (void *data, int state);  
        int  (*getsda) (void *data);  
        int  (*getscl) (void *data);  
        int  (*pre_xfer)  (struct i2c_adapter *);
        void (*post_xfer) (struct i2c_adapter *);
        
        /* local settings */  
        int udelay;     /* half clock cycle time in us, 
                       minimum 2 us for fast-mode I2C, 
                       minimum 5 us for standard-mode I2C and SMBus, 
                       maximum 50 us for SMBus */  
        int timeout;        /* in jiffies */  
    };  
    

    3、将所需数据中保存到设备中

        pdata = &priv->pdata;
    
        if (pdev->dev.of_node) {
            pdata->sda_pin = sda_pin;
            pdata->scl_pin = scl_pin;
            of_i2c_gpio_get_props(pdev->dev.of_node, pdata);
        } else {
            memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata));
        }
    

    4、然后是根据struct i2c_gpio_platform_data结构中定义的数据对struct i2c_algo_bit_data规定具体操作I2C引脚的方法。

        if (pdata->sda_is_open_drain) {
            gpio_direction_output(pdata->sda_pin, 1);
            bit_data->setsda = i2c_gpio_setsda_val;
        } else {
            gpio_direction_input(pdata->sda_pin);
            bit_data->setsda = i2c_gpio_setsda_dir;
        }
    
        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
            gpio_direction_output(pdata->scl_pin, 1);
            bit_data->setscl = i2c_gpio_setscl_val;
        } else {
            gpio_direction_input(pdata->scl_pin);
            bit_data->setscl = i2c_gpio_setscl_dir;
        }
    
        if (!pdata->scl_is_output_only)
            bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;
    

    配置I2C时钟属性

    接下来是I2C时钟频率和超时设置:如果在struct i2c_gpio_platform_data结构中定义了值,那么就采用定义的值,否则就采用默认的值。

        if (pdata->udelay)
            bit_data->udelay = pdata->udelay;
        else if (pdata->scl_is_output_only)
            bit_data->udelay = 50;          /* 10 kHz */
        else
            bit_data->udelay = 5;           /* 100 kHz */
    
        if (pdata->timeout)
            bit_data->timeout = pdata->timeout;
        else
            bit_data->timeout = HZ / 10;        /* 100 ms */
    

    然后是对struct i2c_adapter结构的一些赋值操作:

    指定它的父设备为这里的平台设备,前面在平台设备中定义的一个id(值为0),这里用到了,赋给了struct i2c_adapter中的nr成员,这个值表示总线号。

    这里的总线号和硬件无关,只是在软件上的区分。

        bit_data->data = pdata;
    
        adap->owner = THIS_MODULE;
        if (pdev->dev.of_node)
            strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
        else
            snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
    
        adap->algo_data = bit_data;
        adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        adap->dev.parent = &pdev->dev;
        adap->dev.of_node = pdev->dev.of_node;
    
        adap->nr = pdev->id;
    

    i2c_bit_add_numbered_bus

    到了最后的主角:i2c_bit_add_numbered_bus

    定义在drivers/i2c/algos/i2c-algo-bit.c

    int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
    {
        return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
    }
    EXPORT_SYMBOL(i2c_bit_add_numbered_bus);
    

    i2c_bit_add_numbered_bus实际上是调用了__i2c_bit_add_bus,同时传入了i2c_add_numbered_adapter作为参数。

    __i2c_bit_add_bus

    函数原型:

    /*
     * registering functions to load algorithms at runtime
     */
    static int __i2c_bit_add_bus(struct i2c_adapter *adap,
                     int (*add_adapter)(struct i2c_adapter *))
    {
        struct i2c_algo_bit_data *bit_adap = adap->algo_data;
        int ret;
        
    	// ...
    
        /* register new adapter to i2c module... */
        adap->algo = &i2c_bit_algo; // 添加这个设备的读写实现方法
        adap->retries = 3;
        if (bit_adap->getscl == NULL)
            adap->quirks = &i2c_bit_quirk_no_clk_stretch;
        
    	// 添加适配器,以i2c 总线的方式进行添加
        ret = add_adapter(adap);
        if (ret < 0)
            return ret;
    
        // ...
        
        return 0;
    }
    

    1、添加:指定 该类的对象algo是i2c_adapter的一个域,其中的

    adap->algo= &i2c_bit_algo;
    

    来看这个结构定义

    // include/linux/i2c.h
    struct i2c_algorithm {
        /* If an adapter algorithm can't do I2C-level access, set master_xfer
           to NULL. If an adapter algorithm can do SMBus access, set
           smbus_xfer. If set to NULL, the SMBus protocol is simulated
           using common I2C messages */
        /* master_xfer should return the number of messages successfully
           processed, or a negative value on error */
        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);
    
        /* To determine what the adapter supports */
        u32 (*functionality) (struct i2c_adapter *);
    
    #if IS_ENABLED(CONFIG_I2C_SLAVE) // 从设备相关,先不纠结
        int (*reg_slave)(struct i2c_client *client);
        int (*unreg_slave)(struct i2c_client *client);
    #endif
    };
    
    /* -----exported algorithm data: -------------------------------------  */
    const struct i2c_algorithm i2c_bit_algo = {
        .master_xfer    = bit_xfer,
        .functionality  = bit_func,
    };
    EXPORT_SYMBOL(i2c_bit_algo);
    

    除了注释以外,只是指定了adapter的行为:

    • master_xfer指定为主机的数据传输方式(包括读与写):master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调,以完成i2c的读写
    • functionality:告知这个adapter能够支持的操作。

    i2c数据传输

    具体来看bit_xfer这个函数,这个函数和I2C协议相关,因此理解的前提是搞懂I2C协议。

    根据I2C协议规定,要先发送起始信号,才能开始进行数据的传输,最后数据传输完成后发送停止信号。

    static int bit_xfer(struct i2c_adapter *i2c_adap,
                struct i2c_msg msgs[], int num)
    {
        struct i2c_msg *pmsg;
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
        int i, ret;
        unsigned short nak_ok;
    
        // ...
        
        i2c_start(adap);
        
        for (i = 0; i < num; i++) {
            pmsg = &msgs[i];
            nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
            if (!(pmsg->flags & I2C_M_NOSTART)) {
                if (i) {
                    bit_dbg(3, &i2c_adap->dev, "emitting "
                        "repeated start condition
    ");
                    i2c_repstart(adap);
                }
                ret = bit_doAddress(i2c_adap, pmsg);
                if ((ret != 0) && !nak_ok) {
                    bit_dbg(1, &i2c_adap->dev, "NAK from "
                        "device addr 0x%02x msg #%d
    ",
                        msgs[i].addr, i);
                    goto bailout;
                }
            }
            if (pmsg->flags & I2C_M_RD) {
                /* read bytes into buffer*/
                ret = readbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "read %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
            } else {
                /* write bytes from buffer */
                ret = sendbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
            }
        }
        ret = i;
        
    bailout:
        bit_dbg(3, &i2c_adap->dev, "emitting stop condition
    ");
        i2c_stop(adap);
    
        if (adap->post_xfer)
            adap->post_xfer(i2c_adap);
        return ret;
    }
    
    发送起始信号
    i2c_start(adap);
    

    看这个函数前,先看I2C协议怎么定义起始信号的:起始信号就是在SCL为高电平期间,SDA从高到低的跳变。

    再来看代码是怎么实现的

    /* --- setting states on the bus with the right timing: --------------- */
    
    #define setsda(adap, val)   adap->setsda(adap->data, val)
    #define setscl(adap, val)   adap->setscl(adap->data, val)
    #define getsda(adap)        adap->getsda(adap->data)
    #define getscl(adap)        adap->getscl(adap->data)
    
    static void i2c_start(struct i2c_algo_bit_data *adap)  
    {  
        /* assert: scl, sda are high */  
        setsda(adap, 0);  
        udelay(adap->udelay);  
        scllo(adap);  
    }  
    

    注意,这些 setsda和 setscl这些都是使用的总线的函数,在这里是使用的 i2c-gpio.c中定义的函数,例如:

    // i2c-gpio.c
    
    /*
     * Toggle SDA by changing the output value of the pin. This is only
     * valid for pins configured as open drain (i.e. setting the value
     * high effectively turns off the output driver.)
     */
    static void i2c_gpio_setsda_val(void *data, int state)
    {
        struct i2c_gpio_platform_data *pdata = data;
    
        gpio_set_value(pdata->sda_pin, state);
    }
    
    static int i2c_gpio_probe(struct platform_device *pdev) 
    {
        // ...
        
        if (pdata->sda_is_open_drain) {
            gpio_direction_output(pdata->sda_pin, 1);
            bit_data->setsda = i2c_gpio_setsda_val;
        } else {
            gpio_direction_input(pdata->sda_pin);
            bit_data->setsda = i2c_gpio_setsda_dir;
        }
        // ...
    }
    
    循环处理

    往下是个大的for循环:判断pmsg->flags设置而采取不同的行动。

    回顾一下i2c_msg原型

    // include/linux/i2c.h
    
    #define I2C_M_TEN           0x0010  /* this is a ten bit chip address */  //表示10位设备地址
    #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 */  
    
    struct i2c_msg {  
        __u16 addr;   /* I2C设备地址  */  
        __u16 flags;  /* 标志位       */
        __u16 len;    /* 数据的长度    */  
        __u8 *buf;    /* 数据         */  
    };  
    

    有了上面的基础,再看看下面的for就简单了:

    	// num代表有几个 struct i2c_msg
    	for (i = 0; i < num; i++) { 
            pmsg = &msgs[i];
            nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
            if (!(pmsg->flags & I2C_M_NOSTART)) {
                if (i) {
                    bit_dbg(3, &i2c_adap->dev, "emitting "
                        "repeated start condition
    ");
                    i2c_repstart(adap);
                }
                ret = bit_doAddress(i2c_adap, pmsg);
                if ((ret != 0) && !nak_ok) {
                    bit_dbg(1, &i2c_adap->dev, "NAK from "
                        "device addr 0x%02x msg #%d
    ",
                        msgs[i].addr, i);
                    goto bailout;
                }
            }
            if (pmsg->flags & I2C_M_RD) {
                /* read bytes into buffer*/
                ret = readbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "read %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
            } else {
                /* write bytes from buffer */
                ret = sendbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
            }
        }
        ret = i;
    

    我们来看看这3个if,分别实现了:发送地址、发送或读取字节数据

    发送地址
            if (!(pmsg->flags & I2C_M_NOSTART)) {
                if (i) {
                    bit_dbg(3, &i2c_adap->dev, "emitting "
                        "repeated start condition
    ");
                    i2c_repstart(adap);
                }
                ret = bit_doAddress(i2c_adap, pmsg);
                if ((ret != 0) && !nak_ok) {
                    bit_dbg(1, &i2c_adap->dev, "NAK from "
                        "device addr 0x%02x msg #%d
    ",
                        msgs[i].addr, i);
                    goto bailout;
                }
            }
    

    判断这个设备是否定义了I2C_M_NOSTART标志,这个标志主要用于读写操作

    • 写:不必重新发送起始信号和设备地址。
    • 读:要调用i2c_repstart这个函数去重新发送起始信号,调用bit_doAddress函数去重新构造设备地址字节
    重新构造设备地址字节
    /* doAddress initiates the transfer by generating the start condition (in
     * try_address) and transmits the address in the necessary format to handle
     * reads, writes as well as 10bit-addresses.
     * returns:
     *  0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
     * -x an error occurred (like: -ENXIO if the device did not answer, or
     *  -ETIMEDOUT, for example if the lines are stuck...)
     */
    static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    {
        unsigned short flags = msg->flags;
        unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
        unsigned char addr;
        int ret, retries;
    
        retries = nak_ok ? 0 : i2c_adap->retries;
    
        if (flags & I2C_M_TEN) { // 判断是否是10位地址
            // ...
        } else {        /* normal 7bit address  */
            addr = msg->addr << 1;
            if (flags & I2C_M_RD)
                addr |= 1;
            if (flags & I2C_M_REV_DIR_ADDR)
                addr ^= 1;
            ret = try_address(i2c_adap, addr, retries);
            if ((ret != 1) && !nak_ok)
                return -ENXIO;
        }
    
        return 0;
    }
    

    1、这里先做了一个判断, 10位设备地址和 7位设备地址分别做不同的处理,通常一条 I2C总线上不会挂那么多 I2C设备,所以 10位地址不常用,直接看对 7位地址的处理。

    2、 struct i2c_msg中 addr中是真正的设备地址,而这里发送的 addr高 7位才是设备地址,最低位为读写位。

    • 如果为读,最低位为 1
    • 如果为写,最低位为 0。

    3、所以要将 struct i2c_msg中 addr向左移 1位,再根据读写情况,对最低位进行置位/清零。

    4、最后调用 try_address函数将这个地址字节发送出去。

    地址的发送

    这里负责发送,以及超时重传

    /* try_address tries to contact a chip for a number of
     * times before it gives up.
     * return values:
     * 1 chip answered
     * 0 chip did not answer
     * -x transmission error
     */
    static int try_address(struct i2c_adapter *i2c_adap,
                           unsigned char addr, int retries)
    {
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
        int i, ret = 0;
    
        for (i = 0; i <= retries; i++) {
            // 输出字节
            ret = i2c_outb(i2c_adap, addr);
            if (ret == 1 || i == retries)
                break;
            bit_dbg(3, &i2c_adap->dev, "emitting stop condition
    ");
            i2c_stop(adap);
            udelay(adap->udelay);
            yield();
            bit_dbg(3, &i2c_adap->dev, "emitting start condition
    ");
            i2c_start(adap);
        }
        if (i && ret)
            bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
                    "0x%02x: %s
    ", i + 1,
                    addr & 1 ? "read from" : "write to", addr >> 1,
                    ret == 1 ? "success" : "failed, timeout?");
        return ret;
    }
    

    最主要的就是调用i2c_outb发送一个字节,retries为重复次数,看前面adap->retries= 3;

    如果发送失败,也就是设备没有给出应答信号,那就发送停止信号,发送起始信号,再发送这个地址字节,这就叫retries。

    来看这个具体的i2c_outb函数

    /* send a byte without start cond., look for arbitration,
       check ackn. from slave */
    /* returns:
     * 1 if the device acknowledged
     * 0 if the device did not ack
     * -ETIMEDOUT if an error occurred (while raising the scl line)
     */
    static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
    {
        int i;
        int sb;
        int ack;
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
        /* assert: scl is low */
        for (i = 7; i >= 0; i--) {
            sb = (c >> i) & 1;
            setsda(adap, sb);
            udelay((adap->udelay + 1) / 2);
            if (sclhi(adap) < 0) { /* timed out */
                bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
                    "timeout at bit #%d
    ", (int)c, i);
                return -ETIMEDOUT;
            }
    
            scllo(adap);
        }
        sdahi(adap);
        if (sclhi(adap) < 0) { /* timeout */
            bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
                "timeout at ack
    ", (int)c);
            return -ETIMEDOUT;
        }
    
        /* read ack: SDA should be pulled down by slave, or it may
         * NAK (usually to report problems with the data we wrote).
         */
        ack = !getsda(adap);    /* ack: sda is pulled low -> success */
        bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s
    ", (int)c,
            ack ? "A" : "NA");
    
        scllo(adap);
        return ack;
        /* assert: scl is low (sda undef) */
    }
    
    

    这个函数有两个参数,一个是structi2c_adapter代表I2C主机,一个是发送的字节数据。那么I2C是怎样将一个字节数据发送出去的呢,那再来看看协议。

    首先是发送字节数据的最高位,在时钟为高电平期间将一位数据发送出去,最后是发送字节数据的最低位。

    发送完成之后,我们需要一个ACK信号,要不然我怎么知道发送成功没有:

    ACK信号就是在第九个时钟周期时数据线为低,所以在一个字节数据传送完成后,还要将数据线拉高,我们看程序中就是这一句sdahi(adap);等待这个ACK信号的到来,这样一个字节数据就发送完成。

    回到bit_xfer函数中,前面只是将设备地址字节发送出去了,那么接下来就是该读写数据了。

    读取字节

    先看读

            if (pmsg->flags & I2C_M_RD) { // 如果是读则调用readbytes函数去读
                /* read bytes into buffer*/
                ret = readbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "read %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
            } else //... 如果是写则调用sendbytes去写
    

    注意:这里的数据包括操作设备的基地址。

    static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)  
    {  
        int inval;  
        int rdcount = 0;    /* counts bytes read */  
        unsigned char *temp = msg->buf;  
        int count = msg->len;  
        const unsigned flags = msg->flags;  
    
        while (count > 0) {  
            // 读取一个字节
            inval = i2c_inb(i2c_adap);  
            if (inval >= 0) {  
                *temp = inval;  
                rdcount++;  
            } else {   /* read timed out */  
                break;  
            }  
    
            temp++;  
            count--;  
    
            /* Some SMBus transactions require that we receive the 
               transaction length as the first read byte. */  
            if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {  
                if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {  
                    if (!(flags & I2C_M_NO_RD_ACK))  
                        acknak(i2c_adap, 0);  
                    dev_err(&i2c_adap->dev, "readbytes: invalid "  
                            "block length (%d)
    ", inval);  
                    return -EREMOTEIO;  
                }  
                /* The original count value accounts for the extra 
                   bytes, that is, either 1 for a regular transaction, 
                   or 2 for a PEC transaction. */  
                count += inval;  
                msg->len += inval;  
            }  
    
            bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s
    ",  
                    inval,  
                    (flags & I2C_M_NO_RD_ACK)  
                    ? "(no ack/nak)"  
                    : (count ? "A" : "NA"));  
    
            if (!(flags & I2C_M_NO_RD_ACK)) {  
                inval = acknak(i2c_adap, count);  
                if (inval < 0)  
                    return inval;  
            }  
        }  
        return rdcount;  
    }  
    

    其中一个大的while循环,调用i2c_inb去读一个字节,count为数据的长度,单位为多少个字节。

    那就来看i2c_inb函数。按位读取,就可以了。

    static int i2c_inb(struct i2c_adapter *i2c_adap)  
    {  
        /* read byte via i2c port, without start/stop sequence  */  
        /* acknowledge is sent in i2c_read.         */  
        int i;  
        unsigned char indata = 0;  
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;  
      
        /* assert: scl is low */  
        sdahi(adap);  
        for (i = 0; i < 8; i++) {  
            if (sclhi(adap) < 0) { /* timeout */  
                bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit "  
                    "#%d
    ", 7 - i);  
                return -ETIMEDOUT;  
            }  
            indata *= 2;  
            if (getsda(adap))  
                indata |= 0x01;  
            setscl(adap, 0);  
            udelay(i == 7 ? adap->udelay / 2 : adap->udelay);  
        }  
        /* assert: scl is low */  
        return indata;  
    } 
    
    发送字节
    		else {
                /* write bytes from buffer */
                ret = sendbytes(i2c_adap, pmsg);
                if (ret >= 1)
                    bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s
    ",
                        ret, ret == 1 ? "" : "s");
                if (ret < pmsg->len) {
                    if (ret >= 0)
                        ret = -EIO;
                    goto bailout;
                }
    

    再来看 sendbytes函数

    static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    {
        const unsigned char *temp = msg->buf;
        int count = msg->len;
        unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
        int retval;
        int wrcount = 0;
    
        while (count > 0) {
            retval = i2c_outb(i2c_adap, *temp);
    
            /* OK/ACK; or ignored NAK */
            if ((retval > 0) || (nak_ok && (retval == 0))) {
                count--;
                temp++;
                wrcount++;
    
            /* A slave NAKing the master means the slave didn't like
             * something about the data it saw.  For example, maybe
             * the SMBus PEC was wrong.
             */
            } else if (retval == 0) {
                dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.
    ");
                return -EIO;
    
            /* Timeout; or (someday) lost arbitration
             *
             * FIXME Lost ARB implies retrying the transaction from
             * the first message, after the "winning" master issues
             * its STOP.  As a rule, upper layer code has no reason
             * to know or care about this ... it is *NOT* an error.
             */
            } else {
                dev_err(&i2c_adap->dev, "sendbytes: error %d
    ",
                        retval);
                return retval;
            }
        }
        return wrcount;
    }
    

    也是一个大的while循环,同发送地址字节一样,也是调用i2c_outb去发送一个字节,count也是数据长度。类似的代码就不再赘述了。

    发送停止信号

    还是回到bit_xfer函数,数据传输完成后,调用i2c_stop函数发送停止信号。

    bailout:
        bit_dbg(3, &i2c_adap->dev, "emitting stop condition
    ");
        i2c_stop(adap);
    
        if (adap->post_xfer)
            adap->post_xfer(i2c_adap);
        return ret;
    

    我们看停止信号函数怎么去实现的。

    static void i2c_stop(struct i2c_algo_bit_data *adap)
    {
        /* assert: scl is low */
        sdalo(adap);
        sclhi(adap);
        setsda(adap, 1);
        udelay(adap->udelay);
    }
    

    根据i2c的协议,停止信号就是在时钟为高电平期间,数据线从低到高的跳变。

    i2c_stop中,执行的顺序也是先将数据线拉低,将时钟线拉高,最后将数据拉高,这样就够成了一个停止信号。

    i2c_add_numbered_adapter

    刚刚说了,i2c_bit_add_numbered_bus实际上是调用了__i2c_bit_add_bus,同时传入了i2c_add_numbered_adapter作为参数。

    __i2c_bit_add_bus中的添加适配器的操作是由i2c_add_numbered_adapter完成的。

    所以现在看看另外一个函数调用i2c_add_numbered_adapter。

    在i2c_add_numbered_adapter中,向i2c总线上添加了一个adapter。

    // drivers/i2c/i2c-core.c
    
    
    /**
     * i2c_add_adapter - declare i2c adapter, use dynamic bus number
     * @adapter: the adapter to add
     * Context: can sleep
     *
     * This routine is used to declare an I2C adapter when its bus number
     * doesn't matter or when its bus number is specified by an dt alias.
     * Examples of bases when the bus number doesn't matter: I2C adapters
     * dynamically added by USB links or PCI plugin cards.
     *
     * When this returns zero, a new bus number was allocated and stored
     * in adap->nr, and the specified adapter became available for clients.
     * Otherwise, a negative errno value is returned.
     */
    int i2c_add_adapter(struct i2c_adapter *adapter)
    {
        struct device *dev = &adapter->dev;
        int id;
    
        if (dev->of_node) {
            id = of_alias_get_id(dev->of_node, "i2c");
            if (id >= 0) {
                adapter->nr = id;
                return __i2c_add_numbered_adapter(adapter);
            }
        }
    
        // ...
    
        adapter->nr = id;
    
        return i2c_register_adapter(adapter);
    }
    EXPORT_SYMBOL(i2c_add_adapter);
    
    /**
     * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1
     * @adap: the adapter to register (with adap->nr initialized)
     * Context: can sleep
     *
     * See i2c_add_numbered_adapter() for details.
     */
    static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
        int id;
    
        // ...
    
        return i2c_register_adapter(adap);
    }
    
    /**
     * i2c_add_numbered_adapter - declare i2c adapter, use static bus number
     * @adap: the adapter to register (with adap->nr initialized)
     * Context: can sleep
     *
     * This routine is used to declare an I2C adapter when its bus number
     * matters.  For example, use it for I2C adapters from system-on-chip CPUs,
     * or otherwise built in to the system's mainboard, and where i2c_board_info
     * is used to properly configure I2C devices.
     *
     * If the requested bus number is set to -1, then this function will behave
     * identically to i2c_add_adapter, and will dynamically assign a bus number.
     *
     * If no devices have pre-been declared for this bus, then be sure to
     * register the adapter before any dynamically allocated ones.  Otherwise
     * the required bus ID may not be available.
     *
     * When this returns zero, the specified adapter became available for
     * clients using the bus number provided in adap->nr.  Also, the table
     * of I2C devices pre-declared using i2c_register_board_info() is scanned,
     * and the appropriate driver model device nodes are created.  Otherwise, a
     * negative errno value is returned.
     */
    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
        if (adap->nr == -1) /* -1 means dynamically assign bus id */
            return i2c_add_adapter(adap);
    
        return __i2c_add_numbered_adapter(adap);
    }
    EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
    

    无论adap->nr为多少,__i2c_add_numbered_adapter与``i2c_add_adapter最终都调用了i2c_register_adapter`注册这条 I2C总线:

    // i2c-core.c
    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
        int res = -EINVAL;
        
    	// ...
        
        if (!adap->lock_ops)
            adap->lock_ops = &i2c_adapter_lock_ops;
        
    	// ...
    
        /* Set default timeout to 1 second if not already set */
        if (adap->timeout == 0)    adap->timeout = HZ;
        
    	// 初始化,并注册这个I2C总线设备
        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);
    
        dev_dbg(&adap->dev, "adapter [%s] registered
    ", adap->name);
    
        // ...
    
    #ifdef CONFIG_I2C_COMPAT
        res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
                                       adap->dev.parent);
    #endif
        
        /* create pre-declared device nodes */
        of_i2c_register_devices(adap);
        i2c_acpi_register_devices(adap);
        i2c_acpi_install_space_handler(adap);
    
        // 扫描板级信息(由于 nr = 0 ,因此一点会执行)
        if (adap->nr < __i2c_first_dynamic_bus_num)
            i2c_scan_static_board_info(adap);
    
    	// ...
    
        return 0;
        
    out_list:
        // ...
        return res;
    }
    

    看内核代码有时就会这样,会陷入内核代码的汪洋大海中,而拔不出来,直接后果是最后都忘记看这段代码的目的,丧失继续看下去的信心(勿忘初心的重要性)。所以为了避免这样情况出现,所以最好在开始看代码的时候要明确目标,我通过这段代码到底要了解什么东西,主干要抓住,其它枝叶就不要看了。

    关于i2c注册设备比较关键的有下面3点。

    1. 注册这个I2C总线设备
    adap->dev.bus = &i2c_bus_type;  
    adap->dev.type = &i2c_adapter_type;  
    res = device_register(&adap->dev);  
    

    i2c的总线类型 i2c_bus_type的类型为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,  
        .suspend    = i2c_device_suspend,  
        .resume     = i2c_device_resume,  
    };  
    

    看一下它的 match函数:

    static int i2c_device_match(struct device *dev, struct device_driver *drv)  
    {  
        struct i2c_client   *client = i2c_verify_client(dev);  
        struct i2c_driver   *driver;  
      
        if (!client)  
            return 0;  
      	// ...
        driver = to_i2c_driver(drv);  
        /* match on an id table if there is one */  
        if (driver->id_table)  
            return i2c_match_id(driver->id_table, client) != NULL;  
      
        return 0;  
    }  
    

    这个 match函数主要用来匹配我们的 I2C设备和 I2C驱动的,如果匹配成功,最后会调用驱动的 probe函数,来看它如何匹配的。

    static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,  
                            const struct i2c_client *client)  
    {  
        while (id->name[0]) {  
            if (strcmp(client->name, id->name) == 0)  
                return id;  
            id++;  
        }  
        return NULL;  
    }  
    

    就是判断I2C设备的name字段和驱动中id_table中定义的name字段是否相等。

    2.往这条总线上添加设备
    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) {  
            if (devinfo->busnum == adapter->nr  
                && !i2c_new_device(adapter,  
                                   &devinfo->board_info))  
                dev_err(&adapter->dev,  
                        "Can't create device at 0x%02x
    ",  
                        devinfo->board_info.addr);  
        }  
        up_read(&__i2c_board_lock);  
    }  
    

    遍历 __i2c_board_list这条链表。判断要将新的i2c适配器添加到哪里。

    nr就是 i2c总线的总线号,这里可以理解为是在往这条总线上添加设备。

    所以,如果我们要向 I2C注册一个 I2C设备的话,直接向 __i2c_board_list添加一个设备信息就可以了,先来看这个设备信息结构是怎么定义的。

    struct i2c_board_info {  
        char        type[I2C_NAME_SIZE];  
        unsigned short  flags;  
        unsigned short  addr;  
        void        *platform_data;  
        struct dev_archdata *archdata;  
        int     irq;  
    };  
    

    定义这样一个信息呢一般使用一个宏 I2C_BOARD_INFO

    dev_type为设备的名字,前面也说了,这个name一定要和I2C驱动相同。addr为设备的地址。

    /**
     * I2C_BOARD_INFO - macro used to list an i2c device and its address
     * @dev_type: identifies the device type
     * @dev_addr: the device's address on the bus.
     *
     * This macro initializes essential fields of a struct i2c_board_info,
     * declaring what has been provided on a particular board.  Optional
     * fields (such as associated irq, or device-specific platform_data)
     * are provided using conventional syntax.
     */
    #define I2C_BOARD_INFO(dev_type, dev_addr) 
        .type = dev_type, .addr = (dev_addr)
    
    

    定义了这样一组信息之后呢,接下来当然是往链表添加这些信息了。

    int __init  
    i2c_register_board_info(int busnum,  
        struct i2c_board_info const *info, unsigned len)  
    {  
        int status;  
      
        down_write(&__i2c_board_lock);  
      
        /* dynamic bus numbers will be assigned after the last static one */  
        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);  
            if (!devinfo) {  
                pr_debug("i2c-core: can't register boardinfo!
    ");  
                status = -ENOMEM;  
                break;  
            }  
      
            devinfo->busnum = busnum;  
            devinfo->board_info = *info;  
            list_add_tail(&devinfo->list, &__i2c_board_list);  
        }  
      
        up_write(&__i2c_board_lock);  
      
        return status;  
    }  
    

    第一个参数呢需要注意,它是 I2C总线号,一定要和具体的 I2C总线对应。我们看又定义了这样一个结构 struct i2c_devinfo。

    最后是调用list_add_tail往__i2c_board_list这条链表添加设备信息。

    然后是i2c_new_device

    struct i2c_client *  
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  
    {  
        struct i2c_client   *client;  
        int         status;  
      
        /*为I2C设备申请内存*/  
        client = kzalloc(sizeof *client, GFP_KERNEL);  
        if (!client)  
            return NULL;  
      
        /*指定I2C设备的总线*/  
        client->adapter = adap;  
      
        client->dev.platform_data = info->platform_data;  
      
        if (info->archdata)  
            client->dev.archdata = *info->archdata;  
      
        client->flags = info->flags;  
        client->addr = info->addr; /*I2C设备地址*/  
        client->irq = info->irq;  
      
        strlcpy(client->name, info->type, sizeof(client->name));  
      
        /*检查这个地址有没有被设备占用*/  
        /* Check for address business */  
        status = i2c_check_addr(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;  
      
        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),  
                 client->addr);  
        status = device_register(&client->dev); /*注册设备*/  
        if (status)  
            goto out_err;  
      
        dev_dbg(&adap->dev, "client [%s] registered with bus id %s
    ",  
            client->name, dev_name(&client->dev));  
      
        return client;  
      
    out_err:  
        dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "  
            "(%d)
    ", client->name, client->addr, status);  
        kfree(client);  
        return NULL; 
    

    这个函数的功能是新建一个I2C设备并注册它,在I2C子系统中,I2C设备使用结构structi2c_client描述,那么首先要申请内存空间,I2C设备的主机是谁,必须知道挂载到哪条总线上的,然后就是一些赋值操作,最后就是注册设备,那么这个设备就实实在在的挂在到这条总线上了,这也是新的I2C设备注册方式。

    3.i2c_do_add_adapter

    你看说着说着就跑远了

    static int i2c_do_add_adapter(struct device_driver *d, void *data)  
    {  
        struct i2c_driver *driver = to_i2c_driver(d);  
        struct i2c_adapter *adap = data;  
      
        /* Detect supported devices on that bus, and instantiate them */  
        i2c_detect(adap, driver);  
      
        /* Let legacy drivers scan this bus for matching devices */  
        if (driver->attach_adapter) {  
            /* We ignore the return code; if it fails, too bad */  
            driver->attach_adapter(adap);  
        }  
        return 0;  
    }  
    

    前面通过 i2c_scan_static_board_info往 I2C总线上添加设备是新的方式,而这里调用每个 I2C设备驱动的 attach_adapter函数,然后在 attach_adapter函数中去实现设备的注册,这是老的方式, i2c-dev.c中就是采用的这种方式。至此,总线这块就看完了。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    C# -- HttpWebRequest 和 HttpWebResponse 的使用
    C# -- Lambda 表达式的使用
    ASP.NET -- WebForm -- HttpRequest类的方法和属性
    ASP.NET -- WebForm -- HttpResponse 类的方法和属性
    C# -- 索引器、枚举类型
    C#设计模式之七桥接模式(Bridge Pattern)【结构型】
    C#设计模式之六适配器模式(Adapter Pattern)【结构型】
    C#设计模式之五原型模式(Prototype Pattern)【创建型】
    C#设计模式之四建造者模式(Builder Pattern)【创建型】
    C#设计模式之三抽象工厂模式(AbstractFactory)【创建型】
  • 原文地址:https://www.cnblogs.com/schips/p/linux_driver_i2c-gpio.html
Copyright © 2020-2023  润新知