凡科網(wǎng)建網(wǎng)站付費(fèi)鏈接怎么做線上宣傳方式
往期內(nèi)容
I2C子系統(tǒng)專欄:
- I2C(IIC)協(xié)議講解-CSDN博客
- SMBus 協(xié)議詳解-CSDN博客
- I2C相關(guān)結(jié)構(gòu)體講解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客
- 內(nèi)核提供的通用I2C設(shè)備驅(qū)動(dòng)I2c-dev.c分析:注冊篇
- 內(nèi)核提供的通用I2C設(shè)備驅(qū)動(dòng)I2C-dev.c分析:file_ops篇
- 設(shè)備驅(qū)動(dòng)與設(shè)備樹匹配機(jī)制詳解
- 編寫一個(gè)通用的i2c設(shè)備驅(qū)動(dòng)框架
- 編寫一個(gè)通用的i2c控制器驅(qū)動(dòng)框架
總線和設(shè)備樹專欄:
- 總線和設(shè)備樹_憧憬一下的博客-CSDN博客
- 設(shè)備樹與 Linux 內(nèi)核設(shè)備驅(qū)動(dòng)模型的整合-CSDN博客
前言
在上一章節(jié)(編寫一個(gè)通用的i2c控制器驅(qū)動(dòng)框架)講了i2c_adapter。這里再繼之前的文章( 編寫一個(gè)通用的i2c設(shè)備驅(qū)動(dòng)框架)進(jìn)行補(bǔ)充,講一下l2c_cleint的設(shè)備形態(tài)等
1. 兩種設(shè)備形態(tài)
可以看出是有兩種你描述的這兩種設(shè)備形態(tài)反映了設(shè)備通過不同的總線進(jìn)行交互的差異,并且在設(shè)備樹(DTS)和驅(qū)動(dòng)程序的編寫上有不同的表現(xiàn)。根據(jù)設(shè)備的主要功能和數(shù)據(jù)傳輸方式,它們在 Linux 設(shè)備模型中的位置不同,從而影響了 DTS 文件中的結(jié)構(gòu)和驅(qū)動(dòng)程序的設(shè)計(jì)。我們來分析這兩種設(shè)備形態(tài)。
形態(tài) 1: I2C-only 設(shè)備(圖左側(cè)):
- 特點(diǎn):這類設(shè)備和 CPU 之間的所有數(shù)據(jù)交互都是通過 I2C 總線完成的,沒有其他傳輸方式。
- DTS 描述:設(shè)備直接作為 I2C 控制器的子節(jié)點(diǎn)存在。在 DTS 中,設(shè)備的定義緊隨其所在的 I2C 總線節(jié)點(diǎn)之后。例如 PMIC,它是 I2C 總線上的一個(gè)設(shè)備,因此直接在 I2C 節(jié)點(diǎn)的子節(jié)點(diǎn)中定義。
&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";pmic: pf0100@08 {compatible = "fsl,pfuze100";reg = <0x08>;...};
};
- 驅(qū)動(dòng)模型:I2C 核心負(fù)責(zé)處理設(shè)備的創(chuàng)建和注冊。驅(qū)動(dòng)程序是以 I2C slave 設(shè)備的方式來實(shí)現(xiàn),I2C 驅(qū)動(dòng)框架通過
i2c_driver
結(jié)構(gòu)和i2c_device_id
來與設(shè)備匹配。設(shè)備的probe
、remove
等函數(shù)由 I2C 核心進(jìn)行管理。
static const struct i2c_device_id pmic_i2c_ids[] = {{ "pfuze100", 0 },{ /* sentinel */ }
};static struct i2c_driver pmic_driver = {.driver = {.name = "pfuze100",.of_match_table = pmic_of_match,},.probe = pmic_probe,.remove = pmic_remove,.id_table = pmic_i2c_ids,
};
I2C 是主數(shù)據(jù)交互總線,設(shè)備完全依賴 I2C,所有操作通過 I2C 完成。
驅(qū)動(dòng)注冊流程通過 I2C 子系統(tǒng)的 i2c_driver
完成,設(shè)備由 I2C 核心管理。
形態(tài) 2: 復(fù)合設(shè)備(I2C 作為輔助接口,圖右側(cè)):
- 特點(diǎn):這類設(shè)備通過多種方式與 CPU 交互,例如,主要的數(shù)據(jù)傳輸(如音視頻數(shù)據(jù))通過 TDMS 接口,而配置信息或控制信息通過 I2C(DDC)接口。這類設(shè)備的核心功能決定了它們的主要位置在其他總線(如平臺(tái)總線
platform bus
)上,而 I2C 只是作為一個(gè)輔助接口。 - DTS 描述:HDMI 設(shè)備作為
platform device
定義,并且通過ddc-i2c-bus
屬性引用 I2C 總線,用于 DDC(Display Data Channel)接口的 I2C 通信。
&hdmi {ddc-i2c-bus = <&i2c2>;status = "okay";
};
- 驅(qū)動(dòng)模型:在這種情況下,HDMI 控制器是主要設(shè)備,注冊為
platform_device
。I2C 通信是 HDMI 控制器的一部分,用來處理 EDID 等信息讀取。在驅(qū)動(dòng)程序中,I2C 總線是通過 DTS 中的ddc-i2c-bus
屬性傳遞給 HDMI 驅(qū)動(dòng)的,驅(qū)動(dòng)程序會(huì)通過of_find_i2c_adapter_by_node()
獲取并綁定 I2C 控制器。
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {dev_err(dev, "failed to find ddc-i2c-bus node\n");return -ENODEV;
}ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;/* Use ddc for I2C transactions */
I2C 是輔助總線,用于少量數(shù)據(jù)交互(如 EDID、配置信息傳輸),主要功能通過其他總線(如 TDMS)完成。
驅(qū)動(dòng)以 platform_device
形式注冊,I2C 僅作為其中一個(gè)通信接口,I2C 控制器通過設(shè)備樹中的引用綁定。
2. 驅(qū)動(dòng)編寫
根據(jù)上面所說的形態(tài)去編寫設(shè)備不同的i2c_driver
形態(tài)1: 完全依賴I2C的設(shè)備(如PMIC)
- 確定I2C adapter
根據(jù)設(shè)備硬件連接的實(shí)際情況,確定該設(shè)備所從屬的I2C controller,即I2C adapter(在I2C framework中,I2C controller通常被稱為I2C adapter)。例如,PMIC設(shè)備可能連接在i2c1
總線上。 - 在DTS中添加設(shè)備描述
在設(shè)備樹的I2C adapter節(jié)點(diǎn)中,添加I2C從設(shè)備的描述。這個(gè)從設(shè)備的定義格式與普通的platform device一致,例如:
&i2c1 {pmic: pfuze100@08 {compatible = "fsl,pfuze100";reg = <0x08>;// 其他字段...};
};
compatible
字段的使用
DTS 中的compatible
字段用于設(shè)備驅(qū)動(dòng)的匹配和probe
。I2C驅(qū)動(dòng)會(huì)根據(jù)compatible
字段匹配到合適的驅(qū)動(dòng)程序,例如"compatible = "fsl,pfuze100";"
。- 編寫I2C設(shè)備的驅(qū)動(dòng)程序
示例:
static const struct of_device_id pmic_of_match[] = {{ .compatible = "fsl,pfuze100", },{ /* sentinel */ }
};static struct i2c_driver pmic_driver = {.driver = {.name = "pfuze100",.of_match_table = pmic_of_match,},.probe = pmic_probe,.remove = pmic_remove,
};
static int __init i2c_driver_XXX_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&i2c_XXX_driver);
}
module_init(i2c_driver_XXX_init);
-
- 定義并注冊
i2c_driver
:編寫一個(gè)struct i2c_driver
結(jié)構(gòu)體,并使用 i2c_add_driver將其注冊到I2C core中。 - 匹配設(shè)備:在
i2c_driver
結(jié)構(gòu)體中,定義of_match_table
,使其可以通過compatible
字段進(jìn)行匹配。 - 實(shí)現(xiàn)
probe
回調(diào):編寫probe
函數(shù),用于設(shè)備初始化。
- 定義并注冊
- I2C core處理設(shè)備注冊
當(dāng)I2C adapter注冊時(shí),I2C framework的核心代碼會(huì)自動(dòng)為其下的所有I2C從設(shè)備創(chuàng)建struct i2c_client
結(jié)構(gòu)體,并根據(jù)設(shè)備樹中的compatible
字段,匹配合適的i2c_driver
,然后調(diào)用驅(qū)動(dòng)程序的probe
函數(shù)。 - 在
probe
中
當(dāng)設(shè)備被I2C framework匹配到時(shí),調(diào)用此函數(shù)進(jìn)行設(shè)備初始化。主要負(fù)責(zé)獲取設(shè)備資源(如設(shè)備地址、I2C Adapter),設(shè)置硬件寄存器,或者初始化其他系統(tǒng)資源(如注冊字符設(shè)備)。
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{// 打印日志確認(rèn) probe 觸發(fā)printk(KERN_INFO "ap3216c i2c device probe.\n");// 設(shè)置I2C client指針ap3216c_client = client;// 執(zhí)行設(shè)備初始化:如設(shè)置初始寄存器值i2c_smbus_write_byte_data(client, 0x00, 0x03); // 設(shè)置某些寄存器// 注冊字符設(shè)備或其他資源major = register_chrdev(0, "ap3216c", &ap3216c_ops);ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c");return 0;
}
在注冊的ap3216c_ops中,有關(guān)read和write的函數(shù),使用到I2C數(shù)據(jù)有關(guān)的接口就有兩類:
- 一類是以i2c client為參數(shù),進(jìn)行簡單的數(shù)據(jù)收發(fā)。該方法只可以通過標(biāo)準(zhǔn)方式,發(fā)送或者接收一定數(shù)量的數(shù)據(jù)。
- 另一類是以i2c adapter和i2c msg為參數(shù),可以更為靈活的read或者write數(shù)據(jù),包括i2c_transfer。使用該方法可以以struct i2c_msg為參數(shù),一次讀取、或者寫入、或者讀取加寫入,一定數(shù)量的數(shù)據(jù)。
- 具體的函數(shù)去看\Linux-4.9.88\include\linux\i2c.h
形態(tài)2: 復(fù)合型設(shè)備(如HDMI)
- 確定I2C adapter
根據(jù)設(shè)備硬件的連接方式,找到設(shè)備使用的I2C adapter。例如,HDMI 設(shè)備可以使用i2c2
作為其 DDC 通道。 - 將設(shè)備作為平臺(tái)設(shè)備描述
將設(shè)備作為平臺(tái)設(shè)備(platform device
)進(jìn)行描述。其 DTS 節(jié)點(diǎn)并不是直接位于I2C adapter下,而是位于根目錄。例如:
&hdmi {ddc-i2c-bus = <&i2c2>;status = "okay";
};
- 獲取I2C adapter的引用
在DTS描述中使用ddc-i2c-bus
屬性引用I2C adapter節(jié)點(diǎn)。該屬性通過of_parse_phandle
來解析,并在驅(qū)動(dòng)中獲取相應(yīng)的I2C adapter的指針。 - 在
probe
函數(shù)中獲取I2C adapter
在平臺(tái)設(shè)備的probe
函數(shù)中,通過of_parse_phandle
來獲取I2C adapter節(jié)點(diǎn),并通過of_find_i2c_adapter_by_node()
獲取對(duì)應(yīng)的struct i2c_adapter
指針。如下代碼示例:
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {dev_err(dev, "failed to find ddc-i2c-bus node\n");return -ENODEV;
}ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;
- 使用
i2c_transfer
進(jìn)行讀寫操作
獲取I2C adapter之后,便可以使用i2c_transfer()
接口來進(jìn)行I2C讀寫操作。例如,通過DDC接口讀取EDID信息時(shí):
struct i2c_msg msgs[] = {{.addr = ddc_addr,.flags = I2C_M_RD,.len = 128,.buf = edid_buf,},
};ret = i2c_transfer(ddc, msgs, 1); // 讀取EDID數(shù)據(jù)
3. i2c_client
i2c設(shè)備中用i2c_client結(jié)構(gòu)體來指代:
/*** struct i2c_client - represent an I2C slave device* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* @addr: Address used on the I2C bus connected to the parent adapter.* @name: Indicates the type of the device, usually a chip name that's* generic enough to hide second-sourcing and compatible revisions.* @adapter: manages the bus segment hosting this I2C device* @dev: Driver model device node for the slave.* @irq: indicates the IRQ generated by this device (if any)* @detected: member of an i2c_driver.clients list or i2c-core's* userspace_devices list* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter* calls it to pass on slave events to the slave driver.** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/
struct i2c_client {unsigned short flags; /* div., see below */unsigned short addr; /* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* the adapter we sit on */struct device dev; /* the device structure */int irq; /* irq issued by device */struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
flags
:
-
表示I2C設(shè)備的標(biāo)志位。可以包含以下選項(xiàng):
-
I2C_CLIENT_TEN
:表示設(shè)備使用的是十位地址(而不是常見的七位地址)。I2C_CLIENT_PEC
:表示設(shè)備使用SMBus包錯(cuò)誤校驗(yàn)(Packet Error Checking,PEC),這是一種錯(cuò)誤檢測機(jī)制。
addr
:
- I2C設(shè)備的地址。地址位于I2C總線上的低7位或低10位(如果
flags
指示設(shè)備使用十位地址的話)。
name
:
- 一個(gè)字符數(shù)組,表示設(shè)備的名稱,通常是設(shè)備的芯片名稱或類型。名稱盡可能保持通用,以適應(yīng)同一設(shè)備的不同版本或兼容的設(shè)備。
adapter
:
- 指向設(shè)備所在的I2C總線適配器(
i2c_adapter
)的指針。每個(gè)I2C設(shè)備都掛載在一個(gè)I2C適配器上,適配器管理與設(shè)備的通信。
dev
:
- Linux驅(qū)動(dòng)模型中的設(shè)備節(jié)點(diǎn)(
struct device
),用于表示I2C從設(shè)備。這是Linux設(shè)備模型的標(biāo)準(zhǔn)數(shù)據(jù)結(jié)構(gòu),提供了與設(shè)備類、驅(qū)動(dòng)程序以及電源管理等相關(guān)的接口。
irq
:
- 表示設(shè)備所使用的中斷號(hào)(如果該設(shè)備可以生成中斷)。不使用中斷的設(shè)備此字段可能為0或無效值。
detected
:
- 用于鏈表結(jié)構(gòu)中的節(jié)點(diǎn)。在內(nèi)核中,這個(gè)字段用于將該設(shè)備連接到
i2c_driver.clients
列表或i2c-core
的userspace_devices
列表中。
slave_cb
:
- 僅在啟用了 I2C Slave 模式(即
CONFIG_I2C_SLAVE
選項(xiàng)啟用時(shí))時(shí)有效。 - 這是用于適配器從模式的回調(diào)函數(shù)指針。當(dāng)I2C適配器處于從模式時(shí),適配器調(diào)用這個(gè)回調(diào)函數(shù),將從模式的事件傳遞給從設(shè)備的驅(qū)動(dòng)程序。
i2c_client一般是在register adapter的時(shí)候,解析adapter的child node自行創(chuàng)建的,這個(gè)在對(duì)上一章節(jié)中對(duì)i2c_add_adapte函數(shù)進(jìn)行分析的時(shí)候有提到過