Linux字符設備驅動框架(六):Linux IIC總線驅動框架

/************************************************************************************html

*本文爲我的學習記錄,若有錯誤,歡迎指正。node

*本文參考資料: 
算法

*        https://blog.csdn.net/li_wen01/article/details/51657040
數組

*        https://blog.csdn.net/jscese/article/details/44003393
數據結構

*        https://blog.csdn.net/mcgrady_tracy/article/details/51288138框架

*        http://www.javashuo.com/article/p-yhwqmnkr-m.htmlide

*        https://blog.csdn.net/xie0812/article/details/22984527函數

*        https://blog.csdn.net/zqixiao_09/article/details/50916916學習

************************************************************************************/this

1. I2C簡介

(1)IIC物理總線的構成 

IIC總線是由數據線SDA和時鐘SCL構成的串行總線,可發送和接收數據。在CPU與被控IC之間、IC與IC之間進行雙向傳送,最高傳送速率100kbps。

(2)IIC通訊的特色

同步、串行、電平信號、低速率、近距離。

(3)IIC通訊時序

開始信號:SCL爲高電平時,SDA由高電平向低電平跳變,開始傳送數據;

結束信號:SCL爲高電平時,SDA由低電平向高電平跳變,結束傳送數據;

數據傳輸信號:在開始條件之後,時鐘信號SCL的高電平週期期間,數據線SDA的數據有效,即數據能夠被讀走,開始進行讀操做。在時鐘信號SCL的低電平週期期間,數據線SDA的數據才容許改變。

應答信號:接收數據的IC在接收到8bit數據後,向發送數據的IC發出特定的低電平脈衝,表示已收到數據。CPU向受控單元發出一個信號後,等待受控單元發出一個應答信號,CPU接收到應答信號後,根據實際狀況做出是否繼續傳遞信號的判斷。若未收到應答信號,由判斷爲受控單元出現故障。

 

(4)IIC通訊操做(s5pv210)

2. 相關數據結構

2.1 數據結構

(1)i2c_client

Linux內核使用i2c_client來描述一個掛載在I2C總線上的I2C設備。

struct i2c_client { unsigned short  flags;            //標誌位
    unsigned short  addr;        //設備的地址,低7位爲芯片地址
    char name[I2C_NAME_SIZE];     //設備的名稱,最大爲20個字節
    struct  i2c_adapter *adapter;   //依附的適配器i2c_adapter,適配器指明所屬的總線
    struct  i2c_driver *driver;    //指向設備對應的驅動程序
    struct device  dev;         //設備結構體
    int irq;                //設備申請的中斷號
    struct list_head  list;       //鏈接到總線上的全部設備
    struct list_head   detected;   //已經被發現的設備鏈表
    struct completion  released;    //是否已經釋放的完成量
};

(2)i2c_driver

Linux內核使用i2c_driver來描述一個IIC設備的驅動程序。每一個i2c_client對應一個i2c_driver。

struct i2c_driver { int id;                                                  //驅動標識ID
    unsigned int class;                                      //驅動的類型
    int (*attach_adapter)(struct i2c_adapter *);             //當檢測到適配器時調用的函數
    int (*detach_adapter)(struct i2c_adapter*);              //卸載適配器時調用的函數
    int (*detach_client)(struct i2c_client *)   __deprecated;//卸載設備時調用的函數
  
    /*如下是一種新類型驅動須要的函數,這些函數支持IIC設備動態插入和拔出。若是不想支持只實現上面3個。 要不實現上面3個。要麼實現下面5個。不能同時定義*/
    int  (*probe)(struct i2c_client *,const struct  i2c_device_id *);     //新類型設備探測函數
    int  (*remove)(struct i2c_client *);                                   //新類型設備的移除函數
    void (*shutdown)(struct i2c_client *);                                 //關閉IIC設備
    int  (*suspend)(struct  i2c_client *,pm_messge_t mesg);                //掛起IIC設備
    int  (*resume)(struct  i2c_client *);                                  //恢復IIC設備

    int  (*command)(struct i2c_client *client,unsigned int cmd,void *arg);//使用命令使設備完成特殊的功能。相似ioctl()函數

    struct devcie_driver  driver;                                          //設備驅動結構體
    const struct  i2c_device_id *id_table;                                 //設備ID表
    int (*detect)(struct i2c_client *,int  kind,struct  i2c_board_info *);//自動探測設備的回調函數
 
    const  struct i2c_client_address_data          *address_data;          //設備所在的地址範圍
    struct  list_head    clients;                                          //指向驅動支持的設備
};

(3)i2c_adapter

Linux內核使用i2c_adapter來描述一個IIC總線適配器。IIC總線適配器就是SoC內部的IIC總線控制器,在物理上鍊接若干個IIC設備。IIC總線適配器本質上是一個物理設備,其主要功能是完成IIC總線控制器相關的數據通訊。

struct i2c_adapter { struct module *owner;                         //模塊計數
    unsigned  int id;                             //alogorithm的類型,定義於i2c_id.h中
    unsigned  int  class;                         //容許探測的驅動類型
    const struct i2c_algorithm *algo;             //指向適配器的驅動程序
    void *algo_data;                              //指向適配器的私有數據,根據不一樣的狀況使用方法不一樣
    int (*client_register)(struct  i2c_client *); //設備client註冊時調用
    int (*client_unregister(struct  i2c_client *);//設備client註銷時調用
 u8 level; struct  mutex  bus_lock;                      //對總線進行操做時,將得到總線鎖
    struct  mutex  clist_lock ;                   //鏈表操做的互斥鎖
    int timeout;                                  //超時
    int retries;                                  //重撥次數
    struct device dev;                            //指向適配器的設備結構體
    int nr ; struct  list_head      clients;               //鏈接總線上的設備的鏈表
    char name[48];                                //適配器名稱
    struct completion     dev_released;           //用於同步的完成量
};

(4)i2c_algorithm

Linux內核使用i2c_algorithm來描述IIC適配器與IIC設備的通訊方法。

struct i2c_algorithm {   /*傳輸函數指針,指向實現IIC總線通訊協議的函數,用來肯定適配器支持那些傳輸類型 */
  int  (*master_xfer)(struct  i2c_adapter *adap,  struct  i2c_msg *msg, int num);   /*smbus方式傳輸函數指針,指向實現SMBus總線通訊協議的函數。SMBus和IIC之間能夠經過軟件方式兼容,因此這裏提供了一個函數,可是通常都賦值爲NULL*/ 
   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 *); };

2.2 數據結構之間的聯繫

(1)i2c_driver和i2c_client

i2c_client對應真實的IIC物理設備,每一個IIC設備都須要一個i2c_client來描述;而i2c_driver對應一套驅動方法。i2c_driver與i2c_client的關係是一對多,即一個i2c_driver上能夠支持多個同等類型的i2c_client。

(2)i2c_adapter與i2c_algorithm

i2c_adapter對應一個IIC總線適配器(SoC內部的IIC總線控制器),而i2c_algorithm對應一套通訊方法。一個IIC適配器須要i2c_algorithm中提供的通訊函數來控制適配器上產生特定的訪問週期。缺乏i2c_algorithm的i2c_adapter什麼也作不了,所以i2c_adapter中包含其使用i2c_algorithm的指針。

(3)i2c_adapter和i2c_client

i2c_adapter和i2c_client的關係與IIC硬件體系中適配器和設備的關係一致,即i2c_client依附於i2c_adapter,因爲一個適配器上能夠鏈接多個i2c設備,因此i2c_adapter中包含依附於它的i2c_client的鏈表。

3. Linux I2C驅動框架

Linux內核中的IIC總線不一樣於SoC內部的物理IIC總線 ,內核中的IIC總線是虛擬出來的,目的是管理內核中的IIC從設備及其驅動。

Linux的I2C體系結構分爲3個組成部分:

(1)IIC核心

IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法、IIC通訊方法(algorithm)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。

(2)IIC總線驅動

IIC總線驅動是對IIC硬件體系結構中適配器端(SoC內部的IIC總線控制器)的實現。IIC總線驅動主要包含了IIC適配器數據結構i2c_adapter,IIC適配器的通訊方法數據結構i2c_algorithm和控制I2C適配器產生通訊信號的函數。經由IIC總線驅動的代碼,咱們能夠控制IIC適配器以主控方式產生開始位,中止位,讀寫週期,以及以從設備方式被讀寫,產生ACK等。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

(3)IIC設備驅動

IIC設備驅動是對IIC硬件體系結構中設備端的實現,與掛在I2C總線上的具體的設備通信的驅動。經過I2C總線驅動提供的函數,設備驅動能夠忽略不一樣IIC總線適配器的差別,不考慮其實現細節地與硬件設備通信。這部分代碼通常由驅動工程師完成。

3.1 IIC核心

IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法、IIC通訊方法(algorithm)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。

IIC核心的實現代碼:/kernel/drivers/i2c/i2c-core.c。

3.1.1 IIC bus初始化

Linux內核初始化階段,調用i2c_init() 函數來初始化IIC總線。

static int __init i2c_init(void) { int retval; retval = bus_register(&i2c_bus_type);      //註冊IIC總線
    if (retval) return retval;   ... ... retval = i2c_add_driver(&dummy_driver);   //添加一個空驅動,不知爲什麼要添加這個空驅動
    if (retval) goto class_err; return 0;   ... ... return retval; }

i2c_init() 函數中調用bus_register()函數註冊IIC總線。IIC總線定義以下:

struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match,//match方法用來進行 device 和driver 的匹配,在向總線註冊設備或是驅動的的時候會調用此方法
    .probe    = i2c_device_probe,//probe方法在完成設備和驅動的配對以後調用執行
    .remove   = i2c_device_remove, .shutdown = i2c_device_shutdown, .suspend = i2c_device_suspend, .resume = i2c_device_resume, };

IIC總線提供的match方法:match方法用來進行 i2c_driver 和 i2c_client 的匹配,在向總線註冊i2c_driver或i2c_client的的時候會調用此方法。匹配的方法是拿id_table 中的每一項與 i2c_client 的name 進行匹配,若是名字相同則匹配成功。其函數定義以下:

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)//若是IIC驅動的id_table 存在的話,使用i2c_match_id 進行函數進行匹配。
        return i2c_match_id(driver->id_table, client) != NULL; return 0; }

 i2c_driver 和 i2c_client匹配成功後,IIC總線提供的probe方法將被調用執行,即執行i2c_device_probe()函數。實質上,最終調用執行的是IIC設備驅動中的probe函數,即i2c_driver->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; driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; client->driver = driver;      ... ... status = driver->probe(client, i2c_match_id(driver->id_table, client));//調用IIC設備驅動中的probe函數

  if (status) client->driver = NULL; return status; }

3.1.2 IIC核心提供的接口函數

(1)增長/刪除IIC總線適配器

/*增長一個IIC總線適配器*/
int i2c_add_adapter(struct i2c_adapter *adapter);
/*刪除一個IIC總線適配器*/ int i2c_del_adapter(struct i2c_adapter *adap);

(2)增長/刪除IIC從設備驅動

/*增長一個IIC從設備驅動*/
int i2c_add_driver(struct i2c_driver *driver);
/*刪除一個IIC從設備驅動*/ void i2c_del_driver(struct i2c_driver *driver);

(3)IIC數據傳輸

/* *參數: struct i2c_adapter *adap:IIC總線適配器 * struct i2c_msg*msgs: * int num: */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num) ; /*從如下代碼可知,IIC的數據傳輸是調用i2c_adapter->i2c_algorithm->master_xfer完成*/
int i2c_transfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num) { ... ... if (adap->algo->master_xfer) { for (ret = 0, try = 0; try <=adap->retries; try++) { ret = adap->algo->master_xfer(adap, msgs,num); } } ... ... } 

IIC總線上的數據傳輸是以字節爲單位的,有讀和寫兩種通訊模式。IIC子系統爲了實現這種通訊方法,提供了i2c_msg結構,對於每個START信號,都對應一個i2c_msg對象,實際操做中咱們會將全部的請求封裝成一個struct i2c_msg[],一次性將全部的請求經過i2c_transfer()發送給匹配到的client的從屬的adapter,由adapter根據相應的algo域以及master_xfer域經過主機驅動來將這些請求發送給硬件上的設備。

struct i2c_msg { __u16 addr; //IIC從設備地址
    __u16 flags;  //操做標誌位,I2C_M_RD爲讀(1),寫爲0

#define I2C_M_TEN           0x0010    /* this is a ten bit chip address */
#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 */ __u16 len; //傳輸的數據長度,字節爲單位
    __u8 *buf;      //存放read或write的數據的buffer
};

 

3.2 IIC總線驅動

IIC總線驅動是對IIC硬件體系結構中適配器端(SoC內部的IIC總線控制器)的實現。IIC總線驅動主要包含了IIC適配器數據結構i2c_adapter,IIC適配器的通訊方法數據結構i2c_algorithm和控制I2C適配器產生通訊信號的函數。經由IIC總線驅動的代碼,咱們能夠控制IIC適配器以主控方式產生開始位,中止位,讀寫週期,以及以從設備方式被讀寫,產生ACK等。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

IIC總線驅動的核心代碼:/kernel/drivers/i2c/busses/i2c-s3c2410.c(SAMSUNG 2410平臺)。

i2c-s3c2410.c利用platform總線來實現IIC總線驅動,將IIC適配器的硬件信息掛載在platform device,將IIC總線適配器的驅動程序掛載在platform driver下。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

3.2.1 IIC總線適配器的platform device初始化

platform device的對象是IIC總線適配器,即SoC內部的IIC總線控制器。

構建IIC總線控制器的設備信息。

/*IIC總線控制器的硬件資源*/
static struct resource s3c_i2c_resource[] = { [0] = { .start = S3C_PA_IIC,            //IIC總線控制器寄存器開始地址
        .end   = S3C_PA_IIC + SZ_4K - 1//IIC總線控制器寄存器結束地址
        .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_IIC, .end = IRQ_IIC, .flags = IORESOURCE_IRQ, }, }; /*IIC總線控制器的設備信息*/
struct platform_device s3c_device_i2c0 = { .name = "s3c2410-i2c"//名稱,與platform driver匹配時使用
    .id            = 1, .num_resources = ARRAY_SIZE(s3c_i2c_resource), .resource = s3c_i2c_resource, }; /*IIC總線控制器的默認平臺數據*/
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = { .flags = 0, .slave_addr = 0x10//從設備的設備地址
    .frequency    = 400*1000//IIC的時鐘頻率
    .sda_delay    = S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON, }; /*設置IIC總線控制器的平臺數據*/
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_platform_i2c *npd; if (!pd) pd = &default_i2c_data0; npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); if (!npd) printk(KERN_ERR "%s: no memory for platform data\n", __func__); else if (!npd->cfg_gpio) npd->cfg_gpio = s3c_i2c0_cfg_gpio; s3c_device_i2c0.dev.platform_data = npd; }

向內核註冊IIC總線控制器的platform device。

/*開發板的全部設備信息*/
static struct platform_device *smdkc110_devices[] __initdata = { ... ... &s3c_device_i2c0, ... ... } static void __init smdkc110_machine_init(void) { ... ... platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));//向內核註冊開發板的全部設備信息
    s3c_i2c0_set_platdata(NULL);//設置IIC總線控制器的平臺數據,NULL表示使用默認的平臺數據
 ... ... }

3.2.2  IIC總線適配器的platform driver初始化

 platform driver的對象是IIC總線適配器的驅動程序。分析/kernel/driver/i2c/busees/i2c_s3c2410.c。

註冊IIC總線適配器的驅動程序。

從以下代碼可知,IIC總線適配器的驅動註冊函數i2c_adap_s3c_init()被subsys_initcall修飾,則i2c_adap_s3c_init()函數將在內核初始化階段被調度執行。

/*platform driver支持的設備表*/
static struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = TYPE_S3C2410, },   { .name = "s3c2440-i2c", .driver_data = TYPE_S3C2440, },   { }, }; /*使用MODULE_DEVICE_TABLE 宏聲明,s3c24xx_driver_ids 是platform類型的一個設備表*/ MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); /*IIC總線適配器的驅動信息*/
static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, //與IIC總線適配器的platform device匹配
    .driver    =   { .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, }, }; /*註冊IIC總線適配器的驅動*/
static int __init i2c_adap_s3c_init(void) { return platform_driver_register(&s3c24xx_i2c_driver); } subsys_initcall(i2c_adap_s3c_init);

在IIC總線適配器的platform device與platform driver匹配成功以後,platform driver中的probe函數將被調度運行,即s3c24xx_i2c_probe()函數。

s3c24xx_i2c_probe()函數的主要工做是對IIC總線適配器進行硬件初始化,並向內核註冊一個i2c_adapter。

static int s3c24xx_i2c_probe(struct platform_device *pdev) { struct s3c24xx_i2c *i2c; struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; pdata = pdev->dev.platform_data;//獲取IIC總線適配器的平臺數據
 i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);   /*填充i2c變量*/ strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner   = THIS_MODULE; i2c->adap.algo    = &s3c24xx_i2c_algorithm;//初始化IIC總線適配器的控制算法
    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 */
   /*初始化IIC總線適配器的時鐘*/ i2c->dev = &pdev->dev; i2c->clk = clk_get(&pdev->dev, "i2c"); dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); clk_enable(i2c->clk); /* map the registers */
    //獲取IIC適配器的內存資源信息
    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; /* initialise the i2c controller */
    //初始化IIC總線適配器,主要針對相關寄存器進行操做
    ret = s3c24xx_i2c_init(i2c); /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */
    //獲取IIC總線適配器的中斷資源信息
    i2c->irq = ret = platform_get_irq(pdev, 0);   //申請中斷
    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); ret = s3c24xx_i2c_register_cpufreq(i2c); /* 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 = pdata->bus_num;       //向內核註冊一個i2c adapter
    ret = i2c_add_numbered_adapter(&i2c->adap); ... ... return 0; ... ... }
static int s3c24xx_i2c_probe(struct platform_device *pdev)

 

3.3 IIC從設備驅動

在咱們實際開發中,I2C 總線驅動通常芯片原廠會提供,咱們開發通常是設計設備驅動。

IIC從設備驅動是對IIC硬件體系結構中設備端的實現,與掛在I2C總線上的具體的設備通信的驅動。經過I2C總線驅動提供的函數,設備驅動能夠忽略不一樣IIC總線適配器的差別,不考慮其實現細節地與硬件設備通信。這部分代碼通常由驅動工程師完成。

IIC從設備驅動掛載在IIC總線下,IIC總線管理着IIC從設備的設備信息(i2c_client)與設備驅動(i2c_driver)。所以,IIC從設備驅動的編寫分爲兩個部分:註冊IIC從設備信息、編寫IIC從設備驅動程序。

Linux內核提供了struct i2c_board_info與i2c_register_board_info()函數,方便驅動工程師構建IIC從設備的設備信息 。

(1)struct i2c_board_info

 Linux內核封裝了一個i2c_board_info結構體變量,描述一個IIC從設備的基本信息。內核使用i2c_board_info來構建i2c_client。

struct i2c_board_info { char            type[I2C_NAME_SIZE];//設備名稱,對應i2c_client.name
    unsigned short  flags;              //標誌位,對應i2c_client.flags
    unsigned short  addr;               //從設備地址,對應i2c_client.addr
    void           *platform_data;      //平臺數據,對應i2c_client.dev.platform_data
    struct dev_archdata  *archdata;     //對應i2c_client.dev.archdata
#ifdef CONFIG_OF struct device_node   *of_node; #endif
    int   irq;//中斷號,對應i2c_client.irq
};

(2)i2c_register_board_info()函數

 Linux內核維護了一個__i2c_board_list鏈表,用以管理內核中的全部IIC從設備信息。i2c_register_board_info()函數的工做是向__i2c_board_list鏈表添加一條IIC從設備信息。

/* *  參數:  int busnum:IIC總線編號,表示IIC從設備所掛載的IIC適配器編號 *       struct i2c_board_info const *info:IIC從設備信息        unsigned len:i2c_board_info數組的大小,即IIC從設備的個數 */
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { int status; ... ... for (status = 0; len; len--, info++)    { struct i2c_devinfo    *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);  ... ... devinfo->busnum = busnum; devinfo->board_info = *info; list_add_tail(&devinfo->list, &__i2c_board_list);//向鏈表添加一條IIC從設備信息
 } ... ... return status; }

 在i2c_adapter註冊的時候,會利用__i2c_board_list鏈表中的IIC從設備信息(i2c_board_info)來構建i2c_client。具體調用關係以下:

static int s3c24xx_i2c_probe()【IIC總線驅動的probe函數】

  --i2c_add_numbered_adapter()【註冊i2c_adapter】

    --i2c_register_adapter()【實質註冊i2c_adapter】

      --i2c_scan_static_board_info()【掃描__i2c_board_list鏈表】

        --i2c_new_device()【構建i2c_client,並向內核添加IIC從設備】

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client    *client; int status; //申請client內存空間 
  client = kzalloc(sizeof *client, GFP_KERNEL); ... ...      /*****************填充i2c_client*******************/ client->adapter = adap;                         //與i2c_adapter關聯
    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));//填充從設備名稱
 ... ...   /*****************初始化client->dev*****************/ client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type;   ... ... status = device_register(&client->dev);//建立IIC從設備
    
    return client;   ... ... }

IIC從設備的驅動程序的實例,詳見驅動程序實例(六):mpu6050(IIC + cdev)

相關文章
相關標籤/搜索