I2C子系统知识(二)

I2C总线驱动是对适配器端的实现,其含有适配器数据结构struct i2c_adapter,适配器算法数据结构struct i2c_algorithm。I2C设备驱动是对设备端的实现和控制,其含有设备驱动结构i2c_driver和设备客户端结构struct i2c_client。

以下为i2c_driver结构体,里面提供了通用的操作i2c设备的接口函数.


struct i2c_driver {
    unsigned int class;

    /* Notifies the driver that a new bus has appeared or is about to be
     * removed. You should avoid using this if you can, it will probably
     * be removed in a near future.
     */
    int (*attach_adapter)(struct i2c_adapter *); 
    int (*detach_adapter)(struct i2c_adapter *); 

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *); 
    int (*remove)(struct i2c_client *); 

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *); 
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *); 

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *); 
    const unsigned short *address_list;
    struct list_head clients;
};



client端有一条全局链表,用于串联所有i2c的client设备,为__i2c_board_list如何将client链接到__i2c_board_list上去呢?内核提供了另一个数据结构,静态注册挂接在该链表上的结构为

struct i2c_devinfo {
    struct list_head    list;  /*连接指针指向前后设备*/
    int         busnum;    /*所在bus的编号*/
    struct i2c_board_info   board_info;  /*/板级平台信息*/
};

struct i2c_board_info { 
    char        type[I2C_NAME_SIZE];
    unsigned short  flags;  
    unsigned short  addr;
    void        *platform_data;
    struct dev_archdata *archdata;
#ifdef CONFIG_OF
    struct device_node *of_node;
#endif 
    int     irq;
};

i2c_devinfo结构静态注册的信息最后都会被整合集成到client中,形成一个标准的i2c_client设备并注册。



关于SMBus协议的说明:Intel制定了SMBus标准用于低速通讯。SMBus二线接口与I2C接口非常相似。SMBus也使用一条数据线(SMBDATA)和一条时钟线(SMBCLK)实现通讯。I2C接口和SMBus接口的主要区别是最大和最小时钟速度。SMBCLK必须在10kHz和100kHz之间。SMBCLK和SMBDATA线也需要上拉电阻。3V供电时上拉电阻大于8.5k ,5V供电时上拉电阻大于14k 。SMBus工作电压范围在3V和5V之间,大于2.1V为高电平,低于0.8V为低电平。struct i2c_adapter对应于物理上的一个适配器,而struct i2c_algorithm对应于一套通讯方法。i2c_algorithm提供一些控制适配器发送或接收函数,对于i2c总线需要初始化master_xfer函数,对于smbus总线需要初始化smbus_xfer函数master_xfer函数是以i2c_msg为单位进行控制的,其结构如下:


struct i2c_msg {


 __u16 addr;  //从设备地址
 __u16 flags;  //动作标志:读或写
#define I2C_M_TEN 0x10 //器件地址是10Bit
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000 //意味当前i2c_msg不发送start信号
#define I2C_M_REV_DIR_ADDR 0x2000 //把读写标志位反转
#define I2C_M_IGNORE_NAK 0x1000//当前i2c_msg忽略I2C器件的ack和nack信号。
#define I2C_M_NO_RD_ACK  0x0800 //表示在正行读操作时不去ACK
 __u16 len;  //信息长度
 __u8 *buf;  //信息缓冲区首地址

 

};
 
static int __init i2c_init(void)
{
    int retval;
    /*
    * 注册i2c_bus
    */
    retval = bus_register(&i2c_bus_type);
    if (retval)
    return retval;
#ifdef CONFIG_I2C_COMPAT
 /*

* 在sys/class下创建适配器目录
 */
    i2c_adapter_compat_class = class_compat_register("i2c-adapter");
    if (!i2c_adapter_compat_class) {
    retval = -ENOMEM;
    goto bus_err;
}

#endif
 /*
  * 增加一个虚拟的driver
  */
    retval = i2c_add_driver(&dummy_driver);
    if (retval)
    goto class_err;
    return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
    class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
    bus_unregister(&i2c_bus_type);
    return retval;
}
//其中的i2c_bus_type原型为:
struct bus_type i2c_bus_type = {
   .name = "i2c",
   .match = i2c_device_match,
   .probe = i2c_device_probe,
   .remove = i2c_device_remove,
   .shutdown = i2c_device_shutdown,
   .pm = &i2c_device_pm_ops,
};

适配器需要驱动,i2c设备也需要驱动。在驱动端提供统一的添加驱动接口为:i2c_add_driver

static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}


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;

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);
    if (res)
        return res; 

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    mutex_lock(&core_lock);
    bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    mutex_unlock(&core_lock);

    return 0;
}

driver_register函数注册后会去i2c_bus_type的设备链表上匹配设备,调用driver中的i2c_device_match,如果匹配成功将建立标准关联,并且将调用bus端的probe函数初始化这个设备,即函数i2c_device_probe.

bus_for_each_dev函数循环遍历挂接在i2c bus总线上的设备,并对每个设备调用__process_new_driver函数。




根据dev得到 i2c_client,根据drv得到i2c_driver,然后根据driver->id_table->name 与 client->name比较。匹配成功说明由设备找到了对应的驱动程序

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client   *client = i2c_verify_client(dev);/*   检查是否为client类型,而不是adapter类型  */
    struct i2c_driver   *driver;

    if (!client)
        return 0;

    driver = to_i2c_driver(drv);  /*根据device_driver获得i2c_driver结构体指针*/
    /* match on an id table if there is one */
    if (driver->id_table)
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}


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_device_probe

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;
    int status;

    if (!client)
        return 0;
    /*dev->driver指向匹配完成的driver*/
    driver = to_i2c_driver(dev->driver);
    if (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
    /*唤醒该设备*/  
    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");
    /*跳到驱动里,执行驱动程序里的probe函数*/
    status = driver->probe(client, i2c_match_id(driver->id_table, client));
    if (status) {/*失败将清除client指定的driver内容*/
        client->driver = NULL;
        i2c_set_clientdata(client, NULL);
        return status;
    }
    /*电源管理中设置该设备有效*/
    pm_runtime_set_active(dev);
    return status;
}       



下面看一下当找到一个dev后调用的__process_new_driver函数 *
*************************************************************/
static int __process_new_driver(struct device *dev, void *data)
{
       /*
       * 设备的类型如果不是i2c_adapter类型就推出
       * 下面的代码是针对i2c适配器的代码
       */
       if (dev->type != &i2c_adapter_type)
       return 0;
      
      /*
        如果这个设备代表i2c适配器,则调用i2c_do_add_adapter
       此时的data类型为i2c_driver
      */
      return i2c_do_add_adapter(data, to_i2c_adapter(dev));//to_i2c_adapte根据设备得到他的适配器,第一个参数是i2c_driver

}


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);

    /* 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;
}


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;
    
    /* 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;
    
    /* Stop here if the classes do not match 适配器的类型可以为传感器,eeprom,driver类型必须与其匹配*/
    if (!(adapter->class & driver->class))
        goto exit_free;
    
    /* Stop here if the bus doesn't support probing */
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
        if (address_list[0] == I2C_CLIENT_END)
            goto exit_free;
    
        dev_warn(&adapter->dev, "Probing not supported\n");
        err = -EOPNOTSUPP;
        goto exit_free;
    }       
      /*根据地址列表匹配执行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 (err)
            goto exit_free;
    }
exit_free: 
    kfree(temp_client);
    return err;
}   

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;
    
    /* Make sure there is something at this address */
    if (!i2c_default_probe(adapter, addr))
        return 0;
    
    /* Finally call the custom detection function 调用驱动里的detect函数*/
    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;
    }       
            
    /* 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);/*将当前新生成的client添加到driver->client链表的末尾*/
        else
            dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
                info.type, info.addr);
    }   
    return 0;
}       




  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值