前言
方法 | 問題描述 |
---|---|
Linux 3.X.X版本之后,設(shè)備樹+驅(qū)動 | 此方法是比較符合linux驅(qū)動的寫法的。當(dāng)對于不熟悉設(shè)備樹的小伙伴,寫起來比較棘手 |
使用 i2c-tools,并通過腳本或者應(yīng)用程序編寫設(shè)備驅(qū)動(簡單粗暴) | 此方法是將設(shè)備驅(qū)動丟到用戶態(tài)中,對于一些的設(shè)備除了I2C通信還有一些引腳也要控制的,此方法寫起來將非常痛苦 |
直接操作i2c總線驅(qū)動。(簡單粗暴) | 此方法是將設(shè)備驅(qū)動丟到用戶態(tài)中,對于一些的設(shè)備除了I2C通信還有一些引腳也要控制的,此方法寫起來將非常痛苦。他將會操作多個文件 |
- 上面的做法都有些困難及弊端存在,經(jīng)過摸索了一遍Linux的I2C驅(qū)動框架,我發(fā)現(xiàn)可以很精簡的寫一個I2C設(shè)備的設(shè)備驅(qū)動。而且是放在內(nèi)核態(tài)中,這樣處理一下GPIO或者中斷什么的都很方便。
投機(jī)取巧的I2C驅(qū)動
I2C設(shè)備驅(qū)動說明
- 投機(jī)取巧的I2C驅(qū)動是參考I2C總線驅(qū)動代碼實(shí)現(xiàn)的。
- 投機(jī)取巧的I2C驅(qū)動不需要設(shè)備樹,這也讓一些不熟悉設(shè)備樹的小伙伴能編寫一個設(shè)備驅(qū)動。
- 投機(jī)取巧的I2C驅(qū)動精簡,方便理解。
分析I2C總線驅(qū)動說明
-
I2C總線驅(qū)動的代碼在linux的源碼中--i2c-dev.c中。
-
在代碼中可以看到他提供一套文件操作接口,open,read,write,close接口。實(shí)際在上面描述的直接操作i2c總線驅(qū)動的方法,最終就是調(diào)用到這里。
-
通過整個源碼的分析,我們主要看看open和ioctl接口。其中:
-
open接口,代碼分析:通過inode獲取設(shè)備子設(shè)備號,根據(jù)子設(shè)備號獲取I2C適配器。然后申請一個從設(shè)備對象。并將I2C適配器句柄映射到從設(shè)備對象中。
staticinti2cdev_open(structinode*inode,structfile*file)
{
unsignedintminor=iminor(inode);
structi2c_client*client;
structi2c_adapter*adap;
adap=i2c_get_adapter(minor);
if(!adap)
return-ENODEV;
/*Thiscreatesananonymousi2c_client,whichmaylaterbe
*pointedtosomeaddressusingI2C_SLAVEorI2C_SLAVE_FORCE.
*
*Thisclientis**NEVERREGISTERED**withthedrivermodel
*orI2Ccorecode!!Itjustholdsprivatecopiesofaddressing
*informationandmaybeaPECflag.
*/
client=kzalloc(sizeof(*client),GFP_KERNEL);
if(!client){
i2c_put_adapter(adap);
return-ENOMEM;
}
snprintf(client->name,I2C_NAME_SIZE,"i2c-dev%d",adap->nr);
client->adapter=adap;
file->private_data=client;
return0;
}
- ioctl接口(只提取有用信息): 獲取從設(shè)備對象句柄,然后將用戶態(tài)傳輸?shù)膬?nèi)容傳輸?shù)絠2cdev_ioctl_rdwr()接口。i2cdev_ioctl_rdwr()接口是i2c總線驅(qū)動對從設(shè)備操作的進(jìn)一步封裝,我們進(jìn)一步看一下這個函數(shù)。
staticlongi2cdev_ioctl(structfile*file,unsignedintcmd,unsignedlongarg)
{
structi2c_client*client=file->private_data;
unsignedlongfuncs;
......
switch(cmd){
.....
caseI2C_RDWR:
returni2cdev_ioctl_rdwr(client,arg);
......
}
return0;
}
- i2cdev_ioctl_rdwr接口:通過接口可以看出,從用戶態(tài)拷貝數(shù)據(jù),然后通過i2c_transfer接口進(jìn)入從設(shè)備數(shù)據(jù)讀寫,然后判斷標(biāo)志是否讀操作,如果為讀操作,將i2c_transfer接口接收回來的數(shù)據(jù)拷貝到用戶態(tài)。
staticnoinlineinti2cdev_ioctl_rdwr(structi2c_client*client,
unsignedlongarg)
{
structi2c_rdwr_ioctl_datardwr_arg;
structi2c_msg*rdwr_pa;
u8__user**data_ptrs;
inti,res;
if(copy_from_user(&rdwr_arg,
(structi2c_rdwr_ioctl_data__user*)arg,
sizeof(rdwr_arg)))
return-EFAULT;
......
res=i2c_transfer(client->adapter,rdwr_pa,rdwr_arg.nmsgs);
while(i-->0){
if(res>=0&&(rdwr_pa[i].flags&I2C_M_RD)){
if(copy_to_user(data_ptrs[i],rdwr_pa[i].buf,
rdwr_pa[i].len))
res=-EFAULT;
}
kfree(rdwr_pa[i].buf);
}
......
returnres;
}
投機(jī)取巧的I2C驅(qū)動寫法
- 通過i2c總線驅(qū)動的源碼分析,實(shí)際我們的設(shè)備驅(qū)動可以通過這種模仿這個總線驅(qū)動來寫。
- 代碼模板如下:
#include"rice_i2c.h"
#defineCLASS_NAME"rice_i2c"
#defineDEVICE_NAME"rice_i2c"
typedefstruct{
intmajor_number;
structdevice*device;
structclass*class;
structi2c_client*client;
}Rice_Driver;
Rice_Driverrice_drv;
staticinti2c_test(void)
{
structi2c_msgi2c_msg[2]={0};
uint8_treg_addr=0x75;
uint8_tbuff[2]={0};
i2c_msg[0].addr=0x69;
i2c_msg[0].flags=0;
i2c_msg[0].len=1;
i2c_msg[0].buf=(uint8_t*)?_addr;
i2c_msg[1].addr=0x69;
i2c_msg[1].flags=I2C_M_RD;
i2c_msg[1].len=1;
i2c_msg[1].buf=buff;
i2c_transfer(rice_drv.client->adapter,i2c_msg,2);
printk(KERN_ALERT"i2creaddata:0x%02x!!\n",buff[0]);
}
staticint__initrice_i2c_init(void){
structi2c_client*client;
structi2c_adapter*adap;
rice_drv.major_number=register_chrdev(0,DEVICE_NAME,NULL);
if(rice_drv.major_number0){
printk(KERN_ALERT"Registerfail!!\n");
returnrice_drv.major_number;
}
printk(KERN_ALERT"Registesuccess,majornumberis%d\n",rice_drv.major_number);
rice_drv.class=class_create(THIS_MODULE,CLASS_NAME);
if(IS_ERR(rice_drv.class)){
unregister_chrdev(rice_drv.major_number,DEVICE_NAME);
returnPTR_ERR(rice_drv.class);
}
rice_drv.device=device_create(rice_drv.class,NULL,MKDEV(rice_drv.major_number,0),NULL,DEVICE_NAME);
if(IS_ERR(rice_drv.device)){
class_destroy(rice_drv.class);
unregister_chrdev(rice_drv.major_number,DEVICE_NAME);
returnPTR_ERR(rice_drv.device);
}
//1為設(shè)備掛在的i2c總線的子設(shè)備號
adap=i2c_get_adapter(1);
if(!adap)
return-ENODEV;
rice_drv.client=kzalloc(sizeof(*rice_drv.client),GFP_KERNEL);
if(!rice_drv.client){
i2c_put_adapter(adap);
return-ENOMEM;
}
snprintf(rice_drv.client->name,I2C_NAME_SIZE,"i2c-dev%d",adap->nr);
rice_drv.client->adapter=adap;
i2c_test();
printk(KERN_ALERT"ricei2ckoinit!!\n");
return0;
}
staticvoid__exitrice_i2c_exit(void){
device_destroy(rice_drv.class,MKDEV(rice_drv.major_number,0));
class_unregister(rice_drv.class);
class_destroy(rice_drv.class);
unregister_chrdev(rice_drv.major_number,DEVICE_NAME);
i2c_put_adapter(rice_drv.client->adap);
printk(KERN_ALERT"ricei2ckoexit!!\n");
}
module_init(rice_i2c_init);
module_exit(rice_i2c_exit);
MODULE_AUTHOR("RieChen");
MODULE_LICENSE("GPL");
- 運(yùn)行結(jié)果
Registesuccess,majornumberis240
i2creaddata:0x67!!
ricei2ckoinit!!
總結(jié)
- 通過投機(jī)取巧的方法,不需要設(shè)備樹的存在,就可以在內(nèi)核態(tài)中編寫設(shè)備驅(qū)動,而且很靈活。
- 雖然這是一種可以讓我們快速開發(fā)驅(qū)動的方法,但是還是建議大家要去了解框架的邏輯。這樣不僅對自己的編碼能力,以及開發(fā)很有幫助。
- 希望本篇文章能夠幫助到大家。
審核編輯 黃昊宇
-
驅(qū)動
+關(guān)注
關(guān)注
12文章
1820瀏覽量
85110 -
Linux
+關(guān)注
關(guān)注
87文章
11210瀏覽量
208721 -
IIC
+關(guān)注
關(guān)注
11文章
298瀏覽量
38241 -
驅(qū)動開發(fā)
+關(guān)注
關(guān)注
0文章
130瀏覽量
12059
發(fā)布評論請先 登錄
相關(guān)推薦
評論