Linux I2C驅(qū)動注冊詳解
注冊接口
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)注冊的結(jié)構(gòu)體是struct i2c_driver,主要填充name、probe和id_table字段,其中name是驅(qū)動名稱,到時會在/sys/bus/i2c/drivers顯示這個驅(qū)動名稱,probe會在I2C設(shè)備匹配后調(diào)用,不是發(fā)現(xiàn)I2C設(shè)備,即便實際上這個設(shè)備不在,但是代碼里注冊了這個設(shè)備,或者設(shè)備樹里配置了這個設(shè)備,就會被調(diào)用。
driver.of_match_table和 id_table兩個成員列出的是驅(qū)動程序所能支持的所有設(shè)備,其中driver.of_match_table里的設(shè)備,是跟設(shè)備樹匹配的,id_table是跟靜態(tài)注冊是調(diào)用的struct i2c_board_info里的name做比較的。
示例代碼
驅(qū)動注冊:
static int __devinit zsl_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct property *pp = NULL;
uint8_t buf[4];
struct i2c_msg msgs[2] = {{0}, {0}};
int len = sizeof(msgs) / sizeof(msgs[0]);
printk(KERN_INFO "%s: %x\n",__func__,client->addr);
pp = of_find_property(client->dev.of_node, "testdata", NULL);
if (pp)
printk(KERN_INFO "%s: %d:%s \n",__func__,pp->length,(char *)pp->value);
dump_stack();
if (client->adapter)
{
buf[0] = 0;
buf[1] = 4;
buf[2] = 0;
buf[3] = 0;
memset(msgs,0,sizeof(struct i2c_msg)*2);
msgs[0].addr = client->addr;
msgs[0].flags = 0x0;
msgs[0].len = 2;
msgs[0].buf = buf;
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 4;
msgs[1].buf = buf;
if (i2c_transfer(client->adapter, msgs, len) == len)
printk(KERN_INFO "zsl i2c_transfer r: %d:%x %x %x %x\n",len,buf[0],buf[1],buf[2],buf[3]);
buf[0] = 0;
buf[1] = 4;
buf[2] = 7;
buf[3] = 8;
memset(msgs,0,sizeof(struct i2c_msg)*2);
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 4;
msgs[0].buf = buf;
if (i2c_transfer(client->adapter, msgs, 1) == 1)
printk(KERN_INFO "zsl i2c_transfer w: %d:%x %x %x %x\n",1,buf[0],buf[1],buf[2],buf[3]);
buf[0] = 0;
buf[1] = 4;
buf[2] = 0;
buf[3] = 0;
memset(msgs,0,sizeof(struct i2c_msg)*2);
msgs[0].addr = client->addr;
msgs[0].flags = 0x0;
msgs[0].len = 2;
msgs[0].buf = buf;
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 4;
msgs[1].buf = buf;
msleep(100);
len = i2c_transfer(client->adapter, msgs, len);
if (2 == len)
printk(KERN_INFO "zsl i2c_transfer r: %d:%x %x %x %x\n",len,buf[0],buf[1],buf[2],buf[3]);
}
return 0;
}
static void __devexit zsl_i2c_drv_remove(struct i2c_client *client)
{
printk(KERN_INFO "%s: \n",__func__);
}
static const struct i2c_device_id zsl_dev_id_table[] = {
{ "zsl_i2c_dev", 0 },
{}
};//這里的名字很重要,驅(qū)動第一種匹配設(shè)備的方式要用到
static const struct of_device_id zsl_of_match_ids[] = {
{ .compatible = "i2c_name,zsl", .data = NULL },
{ /* END OF LIST */ } //最后一項為空,用于判斷數(shù)組遍歷完成
};
static struct i2c_driver zsl_i2c_driver = {
.driver = {
.name = "zsl_i2c",
.owner = THIS_MODULE,
.of_match_table = zsl_of_match_ids,//設(shè)備樹匹配用
},
.probe = zsl_i2c_drv_probe,
.remove = __devexit_p(zsl_i2c_drv_remove),
.id_table = zsl_dev_id_table, //設(shè)備樹不需要,i2c_register_board_info匹配使用
};
void zsl_i2c_drv_init(void)
{
i2c_add_driver(&zsl_i2c_driver);
}靜態(tài)設(shè)備注冊
static struct i2c_board_info zsl_i2c_dev = {
I2C_BOARD_INFO("zsl_i2c_dev", 0x50),//這個名字很重要,用于匹配 I2C 驅(qū)動
};
static struct i2c_client *zsl_i2c_client;
void zsl_i2c_dev_init(void)
{
i2c_register_board_info(4, &zsl_i2c_dev, 1);
}設(shè)備樹注冊
&i2c4 {
zsli2c0: zsli2c@50 {
compatible = "i2c_name,zsl";
status = "okay";
testdata = "asdfg";
reg = <0x50>;
};
};運行后probe就會被調(diào)用,無論配的是什么地址,0x50設(shè)備存在,0x60設(shè)備不存在,probe都能被調(diào)用,但是0x60的讀不到數(shù)據(jù)。

調(diào)用關(guān)系
probe里的堆棧打印如下:

從芯片廠家的I2C驅(qū)動初始化接口rk3x_i2c_driver_init開始,使用platform_driver_register接口注冊rk3x_i2c_driver,當(dāng)設(shè)備樹里有對應(yīng)的I2C總線時,就會注冊I2C設(shè)備,匹配后調(diào)用這里的probe接口rk3x_i2c_probe。
rk3x_i2c_probe里會初始化 Rockchip I2C控制器,調(diào)用 i2c_register_adapter將 I2C 適配器注冊到內(nèi)核,進(jìn)而調(diào)用of_i2c_register_devices 掃描設(shè)備樹,調(diào)用i2c_new_client_device創(chuàng)建配置的I2C設(shè)備。
i2c_new_client_device里會創(chuàng)建struct i2c_client并調(diào)用device_register注冊到內(nèi)核,觸發(fā) device_add,內(nèi)核會嘗試匹配驅(qū)動(通過 compatible 字符串或者name)。如果匹配成功,調(diào)用 i2c_device_probe到probe接口的zsl_i2c_drv_probe。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux下SVN服務(wù)器同時支持Apache的http和svnserve獨立服務(wù)器兩種模式且使用相同的訪問權(quán)限賬號
這篇文章主要介紹了Linux下SVN服務(wù)器同時支持Apache的http和svnserve獨立服務(wù)器兩種模式且使用相同的訪問權(quán)限賬號,需要的朋友可以參考下2015-07-07
Ubuntu無網(wǎng)絡(luò)連接及標(biāo)識的解決方法
本文已解決 Ubuntu無網(wǎng)絡(luò)連接/無網(wǎng)絡(luò)標(biāo)識解決方法的相關(guān)問題,并總結(jié)提出了幾種可用解決方案,如果有遇到相同問題的朋友可以參考閱讀下本文,對解決問題有一定的幫助2024-09-09
centos7利用yum安裝lnmp的教程(linux+nginx+php7.1+mysql5.7)
lnmp相信不用多介紹了,大家應(yīng)該都知道,下面這篇文章主要給大家介紹了關(guān)于centos7利用yum安裝lnmp(linux+nginx+php7.1+mysql5.7)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2018-03-03
Linux UDP網(wǎng)絡(luò)編程套接字sockets介紹
文章主要講解了IP地址、端口號、Socket及TCP/UDP協(xié)議的基礎(chǔ)知識,重點包括地址結(jié)構(gòu)、字節(jié)序轉(zhuǎn)換、網(wǎng)絡(luò)通信模型與實現(xiàn),強(qiáng)調(diào)Socket作為應(yīng)用層與網(wǎng)絡(luò)層交互的接口,以及TCP可靠傳輸與UDP高效但不可靠傳輸?shù)膮^(qū)別2025-08-08

