i2c_client,i2c_adapter和I2C-core的簡介

I2C的主要有兩大數據結構,struct i2c_client 和 struct i2c_adapter。

  2.1 i2c_client

     struct i2c_client {
        unsigned short flags;  
        unsigned short addr;  
        char name[I2C_NAME_SIZE];
        struct i2c_adapter *adapter
        struct i2c_driver *driver
        struct device dev;  
        int irq;   
        char driver_name[KOBJ_NAME_LEN];
        struct list_head list;  
        struct completion released;
      };

    struct i2c_client代表一個掛載到i2c總線上的i2c從設備,該設備所需要的數據結構,其中包括

  • 該i2c從設備所依附的i2c主設備 struct i2c_adapter *adapter
  • 該i2c從設備的驅動程序struct i2c_driver *driver
  • 作爲i2c從設備所通用的成員變量,比如addr, name等
  • 該i2c從設別驅動所特有的數據,依附於dev->driver_data下

  2.2 i2c_adapter

     struct i2c_adapter {
        struct module *owner;
        unsigned int id;
        unsigned int class;
        const struct i2c_algorithm *algo;
        void *algo_data;

        ... ...

      };
     struct i2c_adapter代表主芯片所支持的一個i2c主設備,該設備所需要的數據結構,

     其中,struct i2c_algorithm *algo是該i2c主設備傳輸數據的一種算法,或者說是在i2c總線上完成主從設備間數據通信的一種能力。

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

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

  
接下來,要實現整個i2c子系統的驅動,便圍繞這兩個數據結構展開,其主要步驟可總結爲以下三步,

  • 實現i2c主設備驅動                (drivers/i2c/bus

    static int __init tegra_i2c_init_driver(void)
    {
        return platform_driver_register(&tegra_i2c_driver);
    }

    static struct platform_driver tegra_i2c_driver = {
        .probe   = tegra_i2c_probe,
        .remove  = tegra_i2c_remove,
        .driver  = {
            .name  = "tegra-i2c",
            .owner = THIS_MODULE,
            .pm    = TEGRA_I2C_DEV_PM_OPS,
        },
    };


     static int tegra_i2c_probe(struct platform_device *dev)
    {
     struct tegra_i2c_dev *i2c_dev;
      i2c->adap.algo = tegra_i2c_algo;             // 提供該i2c主設備與從設備間完成數據通信的能力

      i2c_add_numbered_adapter(&i2c->adap);      // 調用i2c-core.c中的接口函數,完成該i2c_adapter和i2c_client的註冊

      ... ...
     }

    static const struct i2c_algorithm tegra_i2c_algo = {
        .master_xfer    = tegra_i2c_xfer,  // 根據tegra具體芯片的要求,完成i2c數據傳輸


        .functionality    = tegra_i2c_func,
    };


     

     4 I2C-core

       內核目錄drivers/i2c下的i2c-core.c,顧名思義,是內核爲I2C提供的統一系統接口。

       看看i2c_add_numbered_adapter做了些什麼,

       int i2c_add_numbered_adapter(struct i2c_adapter *adap)
       {
          ... ...
          status = i2c_register_adapter(adap);
          return status;
        }

     

    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
       ... ...

      device_register(&adap->dev);     //完成I2C主設備adapter的註冊,即註冊object和發送uevent等

      i2c_scan_static_board_info(adap);      

       ... ...
    }

     i2c_scan_static_board_info(adap),此函數爲整個I2C子系統的核心,它會去遍歷一個由I2C從設備組成的雙向循環鏈表,並完成所有I2C從設備的i2c_client的註冊,具體過程如下,

     static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
     struct i2c_devinfo *devinfo;     //已經建立好了的I2C從設備鏈表


     list_for_each_entry(devinfo, &__i2c_board_list, list) {
         i2c_new_device(adapter,&devinfo->board_info);
         ... ...
     }
    }

     struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
       ... ...
       i2c_attach_client(client);
       ... ...

    }
    int i2c_attach_client(struct i2c_client *client)
    {
       ... ...
       device_register(&client->dev);     //完成I2C從設備client的註冊
       ... ...
    }

     那麼,這個I2C從設備組成的雙向循環鏈表,是什麼時候通過什麼方式建立起來的呢?

     以某重力感應設備爲例,

    static void __init saar_init(void)

    {

       ... ...

       i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_bma220_info));

       ... ...

    }

    static struct i2c_board_info saar_i2c_bma220_info[] = {
     {
      .driver_name = "bma220",
      .addr  = 0x0B,
      .irq  = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO15)), 
     },
    };

     

     int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
    {

       ... ...

      struct i2c_devinfo *devinfo;
      devinfo->board_info = *info;
      list_add_tail(&devinfo->list, &__i2c_board_list);    //將I2C從設備加入該鏈表中
       ... ...
    }

     所以,在系統初始化的過程中,我們可以通過 i2c_register_board_info,將所需要的I2C從設備加入一個名爲__i2c_board_list雙向循環鏈表,系統在成功加載I2C主設備adapt後,就會對這張鏈表裏所有I2C從設備逐一地完成 i2c_client的註冊。

    •  

        5 Slave Driver

             如果說硬件方面,I2C主設備已經集成在主芯片內,軟件方面,linux也爲 我們提供了相應的驅動程序,位於drivers/i2c/bus下,那麼接下來I2C從設備驅動就變得容易得多。既然系統加載I2C主設備驅動時已經註冊 了i2c_adapter和i2c_client,那麼I2C從設備主要完成三大任務,

      • 系統初始化時添加以i2c_board_info爲結構的I2C從設備的信息
      • 在I2C從設備驅動程序裏使用i2c_adapter裏所提供的算法,即實現I2C通信。
      • 將I2C從設備的特有數據結構掛在到i2c_client.dev->driver_data下。

      以重力感應裝置爲例,

      static int __init BMA220_init(void)
      {
       return i2c_add_driver(&bma220_driver);
      }

      static struct i2c_driver bma220_driver = { 
       .driver = {
        .owner = THIS_MODULE, 
        .name = "bma220",
       },
       .class  = I2C_CLASS_HWMON,
       .probe  = bma220_probe,
       .remove  = bma220_remove,
      };

      static int bma220_probe(struct i2c_client *client, const struct i2c_device_id *id)
      {
       struct bma220_data *data;     
       i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

       i2c_smbus_read_word_data(client, 0x00);    // i2c-core提供的接口,利用i2c_adapter的算法實現I2C通信
       i2c_set_clientdata(bma220_client, data);      // 將設備的數據結構掛到i2c_client.dev->driver_data下
       misc_register(&bma_device);
       request_irq(client->irq, bma220_irq_handler, IRQF_TRIGGER_RISING, "bma220", &data->bma220);
       bma220_set_en_tt_xyz(0);
       bma220_reset_int();

       ... ...
      }

    • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    • another:


  • 在i2c-core架構中struct i2c_adapter和struct i2c_algorithm是爲適配器服務的,也就是i2c總線控制器驅動。註冊一個適配器驅動就是要把一個struct i2c_adapter 加入到內核中,前面我們說過i2c-core中的i2c_adapter_idr是專門用來管理註冊到i2c-core中的struct i2c_adapter結構的。struct i2c_adapter中有指向具體struct i2c_algorithm的指針。struct i2c_algorithm是i2c的具體操作方法函數。內核中提供了兩個adapter註冊接口,分別爲i2c_add_adapter()和 i2c_add_numbered_adapter().由於在系統中可能存在多個adapter,因爲將每一條I2C總線對應一個編號,下文中稱爲 I2C總線號.這個總線號的PCI中的總線號不同.它和硬件無關,只是軟件上便於區分而已。當在實現的具體struct i2c_algorithm中一般也提供了相類似的接口i2c_xxx_add_numbered_adapter和 i2c_xxx_add_adapter。  對於i2c_add_adapter()而言,它使用的是動態總線號,即由系統給其分析一個總線號,而 i2c_add_numbered_adapter()則是自己指定總線號,如果這個總線號非法或者是被佔用,就會註冊失敗.分別來看一下這兩個函數的代 碼:

    int i2c_add_adapter(struct i2c_adapter *adapter)
    {
     int id, res = 0;

    retry:
     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
      return -ENOMEM;

     mutex_lock(&core_lock);
     
     res = idr_get_new_above(&i2c_adapter_idr, adapter,
        __i2c_first_dynamic_bus_num, &id);
     mutex_unlock(&core_lock);

     if (res < 0) {
      if (res == -EAGAIN)
       goto retry;
      return res;
     }

     adapter->nr = id;
     return i2c_register_adapter(adapter);
    }

    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
     int id;
     int status;

     if (adap->nr & ~MAX_ID_MASK)
      return -EINVAL;

    retry:
     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
      return -ENOMEM;

     mutex_lock(&core_lock);
     
     status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
     if (status == 0 && id != adap->nr) {
      status = -EBUSY;
      idr_remove(&i2c_adapter_idr, id);
     }
     mutex_unlock(&core_lock);
     if (status == -EAGAIN)
      goto retry;

     if (status == 0)
      status = i2c_register_adapter(adap);
     return status;
    }

    至於idr的使用方法,在前節中已經介紹。對比兩個代碼的區別在於前者沒有指定adap->nr,而是從某一起始數字開始動態分配而後者直接指定了nr,如果分配的id不和指定的相等,便返回錯誤。接着我們繼續追蹤i2c_register_adapter。

    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
     int res = 0, dummy;

     mutex_init(&adap->bus_lock);    //初始化保護i2c適配器的互斥鎖
     mutex_init(&adap->clist_lock);  
      //初始化保護adap->clients的鎖
     INIT_LIST_HEAD(&adap->clients);
     //初始化i2c適配器上介入的設備(client)鏈表

     mutex_lock(&core_lock);

     

    //初始化adap->dev然後註冊該設備
     if (adap->dev.parent == NULL) {
      adap->dev.parent = &platform_bus;
      pr_debug("I2C adapter driver [%s] forgot to specify "
        "physical device\n", adap->name);
     }
     sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
     adap->dev.release = &i2c_adapter_dev_release;
     adap->dev.class = &i2c_adapter_class;
     res = device_register(&adap->dev);
     if (res)
      goto out_list;

     dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

    //適配器驅動註冊進內核後,對系統中現有的兩種設備進行綁定。

     
     if (adap->nr < __i2c_first_dynamic_bus_num)
      i2c_scan_static_board_info(adap);

     
     dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
         i2c_do_add_adapter);

    out_unlock:
     mutex_unlock(&core_lock);
     return res;

    out_list:
     idr_remove(&i2c_adapter_idr, adap->nr);
     goto out_unlock;
    }

    內 核中I2C設備的加入有兩種方式,一種是在soc上板級是利用i2c_register_board_info註冊在__i2c_board_list鏈 表上的i2c設備(由__i2c_board保護)。另外一種就是通過i2c_driver驅動加入的設備。下面我們分別看看兩種情況

    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
     struct i2c_devinfo *devinfo;

     mutex_lock(&__i2c_board_lock);
     list_for_each_entry(devinfo, &__i2c_board_list, list) {
      if (devinfo->busnum == adapter->nr
        && !i2c_new_device(adapter,
          &devinfo->board_info))
       printk(KERN_ERR "i2c-core: can't create i2c%d-x\n",
        i2c_adapter_id(adapter),
        devinfo->board_info.addr);
     }
     mutex_unlock(&__i2c_board_lock);
    }

    第一種是對&__i2c_board_list中的每個struct i2c_devinfo對比,若busnum與適配器編號相同則調用i2c_new_device把設備綁定到適配器。

    對 第二種通過設備驅動註冊進的設備調用的函數爲bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter)對i2c總線上的每個驅動調用i2c_do_add_adapter(drv,adap)函數,下面我 們看看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;

     
     i2c_detect(adap, driver); 
     //最終調用i2c_new_device把設備綁定到適配器

     
     if (driver->attach_adapter) {
      
      driver->attach_adapter(adap);
     }
     return 0;
    }

    第 二方式應對的是以驅動方式註冊進系統的i2c設備,因爲i2c驅動由兩種方式因此這裏有有兩種應對方式,而且新舊兩種方式中實現的對應函數也有所不同,新 中以probe函數爲代表,而舊中以attach_adapter和detach_adapter爲代表(舊方法我們在這裏不再追蹤)。

  • 在struct i2c_adapter 註冊到內核中後,內核時怎樣把已經註冊進系統中的i2c設備設備與剛註冊進內核的適配器進行綁定的。分兩種情況一是在板級用 i2c_register_board_info註冊的,其二是通過各種struct i2c_driver註冊的。其中驅動註冊又有兩種方法,一種是新的總線式驅動一種是老式的,這裏我們對老式的方法不做介紹,老式的方法在內核中也慢慢的 消亡。下面我們看看新式的方法中的綁定函數i2c_detect。

    在分析i2c_detect之前先看看一個數據結構struct i2c_client_address_data,內核總有很多的適配器,而且適配器上可以接有很多指定地址的設備,一個驅動對設備的在何地址上是有指導 性的,因此在探測設備時(i2c_detect)我們要參考驅動的提示去做。

    struct i2c_client_address_data {   
     const unsigned short *normal_i2c;                
    //該數組指定對每個適配器上的指定地址都進行探測
     const unsigned short *probe;                          
    //該數組是匹對出現的只對指定適配器的指定地址進行探測,前一個數是適配器後面是指該適配器的上的一個地址
     const unsigned short *ignore;                        
    //在進行normal_i2c探測是看看此中是否忽略,若忽略則放棄探測,其也是成對出現的,前者指適配器後者指地址
     const unsigned short * const *forces;         
     //強制使用的地址,確定某個地址代  表 的設備已經連接在總線上了。這個指針指向一個指針數組,每一個成員所

    //指的內容也是每個適配器號後緊跟一個地址。
    };

    下面可以看i2c_detect的源碼:

    static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    {
     const struct i2c_client_address_data *address_data;
     struct i2c_client *temp_client;
     int i, err = 0;
     int adap_id = i2c_adapter_id(adapter);
     //得到適配器的編號

     address_data = driver->address_data;
     if (!driver->detect || !address_data)    
    //新式驅動這兩個成員必須存在
      return 0;

     
     temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); 
    //分配一個設備結構體
     if (!temp_client)
      return -ENOMEM;
     temp_client->adapter = adapter;
     //讓設備與適配器綁定

     
     if (address_data->forces) {
      const unsigned short * const *forces = address_data->forces;
      int kind;

      for (kind = 0; forces[kind]; kind++) {

    //下面代碼首先在forces存在的情況下對其中適配器編號與現在加入的適配器編號相對應的或者指定任何適配器編號ANY_I2C_BUS項相對應的地址調用i2c_detect_address進行探測
       for (i = 0; forces[kind][i] != I2C_CLIENT_END;
            i += 2) {
        if (forces[kind][i] == adap_id
         || forces[kind][i] == ANY_I2C_BUS) {
         dev_dbg(&adapter->dev, "found force "
          "parameter for adapter %d, "
          "addr 0xx, kind %d\n",
          adap_id, forces[kind][i + 1],
          kind);
         temp_client->addr = forces[kind][i + 1];
         err = i2c_detect_address(temp_client,
          kind, driver);
         if (err)
          goto exit_free;
        }
       }
      }
     }

     

     
     if (!(adapter->class & driver->class))  
    //類型不匹配
      goto exit_free;

     

    //如果adapter不支持I2C_FUNC_SMBUS_QUICK不能夠遍歷這個adapter上面的設備
     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
      if (address_data->probe[0] == I2C_CLIENT_END
       && address_data->normal_i2c[0] == I2C_CLIENT_END) 
    //無需探測
       goto exit_free;

      dev_warn(&adapter->dev, "SMBus Quick command not supported, "
        "can't probe for chips\n");
      err = -EOPNOTSUPP;
      goto exit_free;
     }

     

     

    //probe情況下對匹配的適配器的響應地址進行探測(i2c_detect_address)
     for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
      if (address_data->probe[i] == adap_id
       || address_data->probe[i] == ANY_I2C_BUS) {
       dev_dbg(&adapter->dev, "found probe parameter for "
        "adapter %d, addr 0xx\n", adap_id,
        address_data->probe[i + 1]);
       temp_client->addr = address_data->probe[i + 1];
       err = i2c_detect_address(temp_client, -1, driver);
       if (err)
        goto exit_free;
      }
     }

     

    //對normal_i2c指定的所以地址對每個適配器進行探測,但在ingore中指定的適配器對應的地址不做探測
     for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
      int j, ignore;

      ignore = 0;
      for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
           j += 2) {
       if ((address_data->ignore[j] == adap_id ||
            address_data->ignore[j] == ANY_I2C_BUS)
        && address_data->ignore[j + 1]
           == address_data->normal_i2c[i]) {
        dev_dbg(&adapter->dev, "found ignore "
         "parameter for adapter %d, "
         "addr 0xx\n", adap_id,
         address_data->ignore[j + 1]);
        ignore = 1;
        break;
       }
      }
      if (ignore)
       continue;

      dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
       "addr 0xx\n", adap_id,
       address_data->normal_i2c[i]);
      temp_client->addr = address_data->normal_i2c[i];
      err = i2c_detect_address(temp_client, -1, driver);
      if (err)
       goto exit_free;
     }

     exit_free:
     kfree(temp_client);
     return err;
    }

    i2c_detect就是對驅動傳遞下的設備地址列表類別做相應的處理,具體的探測工作由i2c_detect_address來完成,下面我們看具體的代碼

     

    static int i2c_detect_address(struct i2c_client *temp_client, int kind,
             struct i2c_driver *driver)
    {
     struct i2c_board_info info;
     struct i2c_adapter *adapter = temp_client->adapter;
     int addr = temp_client->addr;
     int err;

     
     if (addr < 0x03 || addr > 0x77) {
      dev_warn(&adapter->dev, "Invalid probe address 0xx\n",
        addr);
      return -EINVAL;
     }

     
     //對適配器中的所有設備進行檢驗,看該地址是否被使用過
     if (i2c_check_addr(adapter, addr))
      return 0;

     
     //當kind <0 時檢查設備上是否有這個設備,而對forces而言並不做物理探測
     //在上個函數的探測中probe 和 normal_i2c都是把該值指定爲 -1
     if (kind < 0) {
      if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
           I2C_SMBUS_QUICK, NULL) < 0)
       return 0;

      
      if ((addr & ~0x0f) == 0x50)
       i2c_smbus_xfer(adapter, addr, 0, 0, 0,
               I2C_SMBUS_QUICK, NULL);
     }

     
     //構建i2c_board_info結構
     memset(&info, 0, sizeof(struct i2c_board_info));
     info.addr = addr;
     err = driver->detect(temp_client, kind, &info);
     if (err) {
      
      return err == -ENODEV ? 0 : err;
     }

     
     //用i2c_board_info結構,調用i2c_new_device進行設備和適配器的綁定。
     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;

      
      dev_dbg(&adapter->dev, "Creating %s at 0xx\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 0xx\n",
        info.type, info.addr);
     }
     return 0;
    }

     首 先,對傳入的參數進行一系列的合法性檢查.另外,如果該adapter上已經有了這個地址的設備了.也會返回失敗.所有adapter下面的設備都是以 adapter->dev爲父結點的.因此只需要遍歷adapter->dev下面的子設備就可以得到當前地址是不是被佔用了.如果kind < 0.還得要adapter檢查該總線是否有這個地址的設備.方法是向這個地址發送一個Read的Quick請求.如果該地址有應答,則說明這個地址上有這 個設備.另外還有一種情況是在24RF08設備的特例。i2c_smbus_xfer是與具體的協議相關的,在後面的章節中再分析。最後我們看看設備也適 配器的綁定函數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;

     client = kzalloc(sizeof *client, GFP_KERNEL);
     if (!client)
      return NULL;

     client->adapter = adap;

     client->dev.platform_data = info->platform_data;

     client->flags = info->flags;
     client->addr = info->addr;
     client->irq = info->irq;

     strlcpy(client->name, info->type, sizeof(client->name));

     
     status = i2c_attach_client(client);
     if (status < 0) {
      kfree(client);
      client = NULL;
     }
     return client;
    }

    分 配一個struct i2c_client,並用i2c_board_info中的信息初始化它,然後調用i2c_attach_client,這個函數對其成員dev做一些 初始化加入內核,然把client加入到adapter的設備列表中。再然後i2c的總線機制就發揮作用了(device_register調用)。這裏 留下一個疑問,當設備驅動先註冊,而適配器後註冊是兩者又是如何綁定的呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章