/************************************************************************************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)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)
(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 *); };
(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的鏈表。
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總線適配器的差別,不考慮其實現細節地與硬件設備通信。這部分代碼通常由驅動工程師完成。
IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法、IIC通訊方法(algorithm)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。
IIC核心的實現代碼:/kernel/drivers/i2c/i2c-core.c。
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; }
(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 };
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總線驅動。
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表示使用默認的平臺數據 ... ... }
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; ... ... }
在咱們實際開發中,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)。