一崭添、前言
I2C總線 是一種常用的總線協(xié)議原茅,在設(shè)備中經(jīng)晨岳看到,比如 sensor员咽、陀螺儀等都是使用 I2C總線毒涧。而 Linux內(nèi)核 也提供成熟的 I2C框架,工程師可以根據(jù)硬件特性直接使用該框架編寫驅(qū)動(dòng)程序贝室。本文將著重闡述 Linux內(nèi)核 關(guān)于 I2C總線 的一些概念和實(shí)現(xiàn)
PS:本文默認(rèn)讀者已經(jīng)熟悉I2C協(xié)議契讲,請(qǐng)不了解I2C協(xié)議的讀者自行查閱其他資料了解
二、I2C總線
2.1 重要概念
在了解 Linux內(nèi)核 的 I2C框架 之前滑频,需要對(duì)框架的一些重要概念進(jìn)行認(rèn)識(shí)捡偏,這樣有助于理解框架的設(shè)計(jì)及思路,加深印象峡迷。
-
i2c_bus_type:i2c_bus_type 是 Linux內(nèi)核設(shè)備框架 中的 總線银伟。該 變量 是一個(gè)全局變量,用于 匹配和刪除I2C設(shè)備和I2C驅(qū)動(dòng) 绘搞,并負(fù)責(zé)提供 匹配規(guī)則彤避。i2c_bus_type 的 i2c_device_match 會(huì)查看 驅(qū)動(dòng) 和 設(shè)備 是否 匹配,如果匹配則通過 i2c_device_probe 調(diào)用 驅(qū)動(dòng)的probe函數(shù)夯辖。
其代碼如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
- i2c_adapter:i2c_adapter 稱為 I2C適配器琉预。所謂 I2C適配器 就是 Soc 上的 I2C控制器,而 i2c_adapter 就是 硬件I2C控制器 的 驅(qū)動(dòng)實(shí)現(xiàn)蒿褂,即實(shí)現(xiàn)了 CPU通過I2C控制器與外界進(jìn)行數(shù)據(jù)交換
struct i2c_adapter {
/* I2C適配器的通信方法 */
const struct i2c_algorithm *algo;
/* I2C適配器的device結(jié)構(gòu)體圆米,表明其也是一個(gè)設(shè)備 */
struct device dev;
};
- i2c_algorithm :i2c_algorithm 是 I2C適配器 的 通信方法 實(shí)現(xiàn),一般通過該結(jié)構(gòu)體實(shí)現(xiàn) 數(shù)據(jù)的發(fā)送和接受啄栓。
struct i2c_algorithm {
/* 主機(jī)發(fā)送函數(shù) */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
/* 從機(jī)發(fā)送函數(shù) */
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_client:i2c_client 就是 I2C設(shè)備娄帖,該結(jié)構(gòu)體描述了 I2C設(shè)備硬件信息
struct i2c_client {
/* 設(shè)備I2C地址 */
unsigned short addr; /* chip address - NOTE: 7bit */
/* 設(shè)備名稱 */
char name[I2C_NAME_SIZE];
/* I2C適配器 */
struct i2c_adapter *adapter; /* the adapter we sit on */
/* device結(jié)構(gòu)體,表明其為一個(gè)設(shè)備 */
struct device dev; /* the device structure */
};
- i2c_driver:i2c_driver 就是 I2C驅(qū)動(dòng)程序昙楚,就是 I2C硬件設(shè)備端的實(shí)現(xiàn)近速。一般掛接在 I2C適配器 上,并通過 I2C適配器 與 CPU 交換數(shù)據(jù)。
struct i2c_driver {
/* 驅(qū)動(dòng)的probe函數(shù) */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
/* 驅(qū)動(dòng)的remove函數(shù) */
int (*remove)(struct i2c_client *);
/* I2C驅(qū)動(dòng)的device_driver結(jié)構(gòu)體数焊,表明其也是一個(gè)設(shè)備驅(qū)動(dòng) */
struct device_driver driver;
/* 驅(qū)動(dòng)ID table永淌,用于匹配 */
const struct i2c_device_id *id_table;
/* 該驅(qū)動(dòng)擁有的I2C地址 */
const unsigned short *address_list;
/* 該驅(qū)動(dòng)擁有的I2C設(shè)備鏈表 */
struct list_head clients;
};
筆者粗略的畫了下他們之間的關(guān)系圖崎场,如下所示:
虛線表示對(duì)應(yīng)關(guān)系佩耳,實(shí)現(xiàn)表示數(shù)據(jù)
- 硬件信息 通過 設(shè)備樹 解析到了 i2c_client
- i2c_driver 的 匹配信息 在 驅(qū)動(dòng)程序 中填充。
- i2c_client 和 i2c_driver 通過 i2c_bus_type 進(jìn)行匹配谭跨,如果成功 i2c_bus_type 調(diào)用 i2c_driver 的 probe函數(shù)
- i2c_driver 通過 i2c_adapter 向外發(fā)送數(shù)據(jù)
2.2 代碼分層
了解完主要的數(shù)據(jù)結(jié)構(gòu)干厚,下面看看 I2C框架 的主體代碼組成,其源碼目錄在 /drivers/i2c螃宙。下分多個(gè)文件和文件夾蛮瞄,包括 I2C框架代碼、不同體系結(jié)構(gòu)的I2C驅(qū)動(dòng)代碼 等谆扎。
I2C框架的主體代碼和目錄 分別如下:
- i2c-core.c:I2C框架核心功能源碼挂捅,該文件使得 I2C框架 實(shí)現(xiàn)了 硬件 與 軟件 的分層。
- i2c-dev.c:該源碼實(shí)現(xiàn)了 I2C適配器設(shè)備文件的功能堂湖,給 I2C適配器 分配一個(gè) 設(shè)備號(hào) 并將其實(shí)現(xiàn)為 /dev目錄 下的 設(shè)備闲先。用戶可以通過該 設(shè)備文件 直接在 應(yīng)用層 操作 Soc的I2C控制器
- busses:不同 Soc的I2C控制器 的驅(qū)動(dòng)代碼。
2.3 代碼講解
2.3.1 I2C驅(qū)動(dòng)注冊(cè)
編寫 I2C設(shè)備驅(qū)動(dòng) 時(shí)一般按照下面幾個(gè)步驟進(jìn)行:
- 使用 i2c_add_driver 添加驅(qū)動(dòng)
- 實(shí)現(xiàn) I2C設(shè)備驅(qū)動(dòng)操作集无蜂,比如 probe伺糠、remove等
- 實(shí)現(xiàn) I2C設(shè)備文件操作集,比如 read斥季、write等
實(shí)現(xiàn)設(shè)備調(diào)用 i2c_add_driver函數(shù) 有多種方式训桶,如下:
- 使用 宏module_i2c_driver 直接添加驅(qū)動(dòng),該宏會(huì)在 驅(qū)動(dòng)裝載 時(shí)自動(dòng)調(diào)用 i2c_add_driver函數(shù)酣倾,其本質(zhì)是注冊(cè) 驅(qū)動(dòng)初始函數(shù) 和 驅(qū)動(dòng)去初始化函數(shù)舵揭。
- 與一般設(shè)備一樣,通過設(shè)備樹或多種方法 手動(dòng)調(diào)用i2c_add_driver
i2c_add_driver 調(diào)用圖譜如下:
i2c_add_driver(宏)
->i2c_register_driver(函數(shù))
->driver_register(想內(nèi)核注冊(cè)驅(qū)動(dòng))
->bus_add_driver(在i2c_bus_type上注冊(cè)驅(qū)動(dòng))
->i2c_for_each_dev(driver, __process_new_driver);(為每個(gè)驅(qū)動(dòng)調(diào)用__process_new_driver函數(shù))
->__process_new_driver
->i2c_do_add_adapter(加驅(qū)動(dòng)添加到對(duì)應(yīng)的I2C適配器)
->i2c_detect(I2C適配器探測(cè)是否存在驅(qū)動(dòng)所對(duì)應(yīng)的I2C設(shè)備)
->i2c_detect_address(I2C適配器使用地址對(duì)I2C設(shè)備進(jìn)行探測(cè))
->i2c_new_device(生成新的i2c_client(用于描述I2C設(shè)備硬件信息)躁锡,并掛接在I2C驅(qū)動(dòng)上)
代碼如下:
#define module_i2c_driver(__i2c_driver) \
module_driver(__i2c_driver, i2c_add_driver, \
i2c_del_driver)
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
....
/* 注冊(cè)i2c_driver的總線為i2c_bus_type */
driver->driver.bus = &i2c_bus_type;
/* 初始化i2c_driver的i2c_client鏈表午绳,一個(gè)driver可以有多個(gè)client,掛接在該鏈表上 */
INIT_LIST_HEAD(&driver->clients);
....
/* 注冊(cè)驅(qū)動(dòng) */
res = driver_register(&driver->driver);
....
/* 對(duì)i2c_driver上的每一個(gè)i2c_client都調(diào)用__process_new_driver */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
下面分為 2個(gè) 階段來闡述注冊(cè)過程:
- driver_register:注冊(cè)驅(qū)動(dòng)
- i2c_for_each_dev:遍歷設(shè)備并調(diào)用 __process_new_driver 創(chuàng)建 i2c_client
下面先看看 driver_register 的代碼流程:
int driver_register(struct device_driver *drv)
{
......
/* 將驅(qū)動(dòng)添加到總線上 */
ret = bus_add_driver(drv);
......
return ret;
}
int bus_add_driver(struct device_driver *drv)
{
/* 將驅(qū)動(dòng)添加到總線的驅(qū)動(dòng)鏈表(bus->p->klist_drivers) */
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
......
if (drv->bus->p->drivers_autoprobe) {
if (driver_allows_async_probing(drv)) {
......
} else {
error = driver_attach(drv);
......
}
}
......
return 0;
}
int driver_attach(struct device_driver *drv)
{
/* 遍歷總線上的所有驅(qū)動(dòng)并調(diào)用__driver_attach */
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, fn)
{
/*
遍歷i2c_bus_type總線的所有設(shè)備鏈表(bus->p->klist_devices)的所有設(shè)備稚铣,執(zhí)行fn函數(shù)
這里的設(shè)備就是i2c_client中的struct device成員
在設(shè)備樹解析時(shí)會(huì)根據(jù)i2c節(jié)點(diǎn)創(chuàng)建i2c_client箱叁,并將其struct device成員鏈入i2c_bus_type的bus->p->klist_devices鏈表
*/
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
int ret;
/* 匹配設(shè)備和驅(qū)動(dòng),如果不成功則返回 */
ret = driver_match_device(drv, dev);
if (ret == 0) {
/* no match */
return 0;
}
......
/* 匹配設(shè)備和驅(qū)動(dòng)成功惕医,調(diào)用probe函數(shù) */
if (!dev->driver)
driver_probe_device(drv, dev);
......
return 0;
}
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
/* 調(diào)用i2c_bus_type總線的macth函數(shù)耕漱,即i2c_device_match函數(shù) */
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
......
driver = to_i2c_driver(drv);
/* 調(diào)用i2c_match_id進(jìn)行配 */
if (i2c_match_id(driver->id_table, client))
return 1;
return 0;
}
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client)
{
/* 匹配驅(qū)動(dòng)的名稱和設(shè)備名稱,并返回結(jié)果 */
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
......
/* 如果匹配到設(shè)備則調(diào)用probe函數(shù) */
ret = really_probe(dev, drv);
......
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
/* 調(diào)用i2c_bus_type的probe函數(shù)抬伺,即i2c_device_probe函數(shù) */
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
}
}
static int i2c_device_probe(struct device *dev)
{
/*
調(diào)用i2c驅(qū)動(dòng)的probe函數(shù)
*/
driver->probe(client, i2c_match_id(driver->id_table, client));
}
到了這里螟够,driver_register 的流程基本結(jié)束,下面主要執(zhí)行 i2c_for_each_dev,代碼流程如下:
int i2c_for_each_dev(void *data, fn)
{
......
/* 對(duì)i2c_bus_type總線上的設(shè)備進(jìn)行遍歷 */
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
......
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int fn)
{
......
/*
找出掛在bus->p->klist_devices鏈表上的首個(gè)設(shè)備(struct device)的迭代器。
這里可能有讀者有疑問赤嚼,這些struct device什么時(shí)鏈入bus->p->klist_devices的呢
在設(shè)備樹解析期間闯估,如果設(shè)備樹的i2c節(jié)點(diǎn)有設(shè)備節(jié)點(diǎn),則會(huì)調(diào)用i2c_new_device萧福。
該函數(shù)會(huì)創(chuàng)建一個(gè)i2c_client,并將該i2c_client的struct device成員鏈入bus->p->klist_devices鏈表
其中的bus就是i2c_bus_type
*/
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
/* 遍歷總線上的設(shè)備鏈表(bus->p->klist_devices)的所有設(shè)備辈赋,調(diào)用fn函數(shù) */
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
......
}
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type)
return 0;
/*
根據(jù)函數(shù)傳遞進(jìn)來的參數(shù)可知鲫忍,data參數(shù)就是i2c_driver結(jié)構(gòu)體
而to_i2c_adapter(dev)用于找出該設(shè)備對(duì)應(yīng)的i2c_adapter。
這里也是同理钥屈,在設(shè)備樹解析時(shí)悟民,會(huì)根據(jù)設(shè)備所在的i2c節(jié)點(diǎn)(設(shè)備樹的i2c節(jié)點(diǎn)對(duì)應(yīng)一個(gè)i2c_控制器)
將設(shè)備鏈入對(duì)應(yīng)的i2c_adapter結(jié)構(gòu)體
*/
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
static int i2c_do_add_adapter(struct i2c_driver *driver,
struct i2c_adapter *adap)
{
/* 使用對(duì)應(yīng)的i2c_adapter探測(cè)驅(qū)動(dòng)所對(duì)應(yīng)的設(shè)備是否存在 */
i2c_detect(adap, driver);
......
return 0;
}
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
......
/* 獲取驅(qū)動(dòng)的I2C地址鏈表 */
address_list = driver->address_list;
/*
如果i2c_driver沒實(shí)現(xiàn)detect函數(shù),那么將會(huì)在這里返回
有些i2c驅(qū)動(dòng)沒有實(shí)現(xiàn)detect函數(shù)篷就,所以注冊(cè)流程到這里就結(jié)束了
*/
if (!driver->detect || !address_list)
return 0;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
......
/* i2c_detect_address會(huì)根據(jù)地址鏈表去探測(cè)是否存在設(shè)備 */
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
......
}
}
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;
......
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
/* 調(diào)用驅(qū)動(dòng)的detecr函數(shù) */
err = driver->detect(temp_client, &info);
if (err) {
/* 失敗就返回-ENODEV */
return err == -ENODEV ? 0 : err;
}
/* Consistency check */
if (info.type[0] == '\0') {
......
} else {
/* 如果探測(cè)成功則使用i2c_new_device創(chuàng)建client并添加到driver的clients鏈表 */
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
return 0;
}
有些 I2C驅(qū)動(dòng) 并不會(huì)實(shí)現(xiàn) detect函數(shù)射亏,所以在知道到 driver_register 時(shí)已經(jīng)完成大部分工作,在 執(zhí)行 i2c_for_each_dev 時(shí)就已經(jīng)早早退出了竭业。
前面的代碼多次提到過 i2c_client結(jié)構(gòu)體 和 struct device結(jié)構(gòu)智润,從前面的代碼講解中我們已經(jīng)知道 設(shè)備樹解析 時(shí)會(huì)根據(jù) I2C節(jié)點(diǎn) 的信息創(chuàng)建對(duì)應(yīng)的 i2c_client 和 struct device,那么這里也提一下如何在 設(shè)備樹 中添加 I2C設(shè)備節(jié)點(diǎn)信息
在 Documentation/devicetree/bindings/i2c 目錄下的 i2c.txt文檔 中可以查看 i2c_adapter 的設(shè)備節(jié)點(diǎn)信息永品,而在同一目錄下存在 多個(gè)Soc平臺(tái) 的 i2c適配器說明做鹰。
I2C設(shè)備 一般是作為 slaver(從機(jī)) 存在,而一般的 I2C從機(jī)節(jié)點(diǎn)信息 有以下屬性:
- compatible:用于 驅(qū)動(dòng)匹配
- reg:I2C設(shè)備 的總線地址
下面為 設(shè)備樹 代碼例子:
i2c0@0xXXXXXXXX {
/* i2c控制器的硬件信息 */
......
/* i2c設(shè)備節(jié)點(diǎn)信息 */
i2c_test: i2c_test@34{
compatible = "xxxx";
/* reg表明該I2C設(shè)備的地址為0x34 */
reg = <0x34>;
};
};
2.3.2 I2C設(shè)備注冊(cè)
在 I2C驅(qū)動(dòng)注冊(cè) 中使用的是默認(rèn)的 自動(dòng)注冊(cè)設(shè)備鼎姐,也就是通過 設(shè)備樹钾麸,由操作系統(tǒng)幫我們完成 設(shè)備的生成和注冊(cè)。
在解析 I2C設(shè)備節(jié)點(diǎn) 時(shí)炕桨,一般調(diào)用的是:
of_i2c_register_device
of_i2c_register_devices
它們最終都會(huì)調(diào)用到 i2c_new_device 來創(chuàng)建 I2C設(shè)備饭尝。
如果在某些場(chǎng)景,比如 設(shè)備樹 沒有 I2C設(shè)備節(jié)點(diǎn)献宫, 想 手動(dòng)注冊(cè)I2C設(shè)備 钥平,那么就可以通過手動(dòng)調(diào)用 i2c_new_device 來創(chuàng)建和注冊(cè) I2C設(shè)備。
其函數(shù)調(diào)用圖譜如下:
->i2c_new_device
->device_register
->device_add
->bus_add_device
->bus_probe_device
->__device_attach
->driver_match_device
->driver_probe_device
struct i2c_client* i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
......
/* 創(chuàng)建完i2c_client后對(duì)其進(jìn)行一列的初始化 */
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));
......
/* 設(shè)備i2c_client的struct device成員 */
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
client->dev.fwnode = info->fwnode;
......
i2c_dev_set_name(adap, client);
......
/* 注冊(cè)shebei */
status = device_register(&client->dev);
......
return client;
}
int device_register(struct device *dev)
{
device_initialize(dev);
/* 添加設(shè)備 */
return device_add(dev);
}
int device_add(struct device *dev)
{
......
/* 將設(shè)備添加是bus上 */
error = bus_add_device(dev);
......
/* 探測(cè)bus上是否有與設(shè)備匹配的驅(qū)動(dòng) */
bus_probe_device(dev);
......
}
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
......
if (bus) {
/* 將設(shè)備添加到bus的klist_devices鏈表歐尚姊途,該成員在注冊(cè)驅(qū)動(dòng)時(shí)會(huì)使用到涉瘾。用于匹配 */
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
}
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
......
if (bus->p->drivers_autoprobe)
/* device_initial_probe直接調(diào)用__device_attach */
device_initial_probe(dev);
......
}
static int __device_attach(struct device *dev, bool allow_async)
{
......
/* 對(duì)bus上的每個(gè)設(shè)備都調(diào)用__device_attach_driver */
ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
......
}
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
......
/* 匹配設(shè)備與驅(qū)動(dòng) */
ret = driver_match_device(drv, dev);
......
/* 匹配成功則調(diào)用probe */
return driver_probe_device(drv, dev);
}
2.4 驅(qū)動(dòng)接口及步驟
2.4.1 I2C框架接口
- 增加/刪除I2C適配器:
- i2c_add_adapter
- i2c_del_adapter
- 增加/刪除I2C驅(qū)動(dòng):
- i2c_add_driver
- i2c_del_driver
- I2C傳輸、發(fā)送和接收
- i2c_transfer(adap, msgs, num):用于 I2C適配器 和 設(shè)備 之間的 一組 消息交互捷兰。注意其傳入的實(shí)例為 I2C適配器
- i2c_master_recv:用于 主機(jī)接收立叛,內(nèi)部調(diào)用 i2c_transfer。注意其傳入的實(shí)例為 I2C設(shè)備(i2c_client)
- i2c_master_send(client, buf, count):用于 主機(jī)發(fā)送贡茅,內(nèi)部調(diào)用 i2c_transfer秘蛇。注意其傳入的實(shí)例為 I2C設(shè)備(i2c_client)
2.4.2 I2C步驟
對(duì) I2C驅(qū)動(dòng) 的移植和編寫主要有 2部分:
-
I2C適配器:完成 I2C適配器 的硬件驅(qū)動(dòng)
- 探測(cè)及初始化 I2C適配器其做,如 申請(qǐng)寄存器地址、申請(qǐng)中斷 等
- 驅(qū)動(dòng) I2C適配器 產(chǎn)生信號(hào)及 中斷處理
- 提供 I2C適配器 的 algorithm(即通信方法)
- 使用 i2c_add_adapter 添加 I2C適配器
-
I2C驅(qū)動(dòng):完成 I2C設(shè)備外設(shè) 的硬件驅(qū)動(dòng)
- 實(shí)現(xiàn) i2c_driver 的接口赁还,如 probe妖泄、remove、suspend艘策、resume 等
- 填充 i2c_device_id表 并賦值給 i2c_driver
- 實(shí)現(xiàn) I2C設(shè)備 的具體方法蹈胡,比如 read、write柬焕、ioctl等
三审残、參考鏈接
Linux I2C驅(qū)動(dòng)框架
Linux i2c 設(shè)備驅(qū)動(dòng)程序框架詳解
I2C中的重復(fù)起始條件到底是什么意思