/************************************************************************************
*本文為個人學習記錄,如有錯誤,歡迎指正。
* 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
* https://www.cnblogs.com/xiaojiang1025/p/6500540.html
* https://blog.csdn.net/xie0812/article/details/22984527
* https://blog.csdn.net/zqixiao_09/article/details/50916916
************************************************************************************/
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; ... ... }
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)。
