前言
方法 | 問題描述 |
---|---|
Linux 3.X.X版本之后,設備樹+驅動 | 此方法是比較符合linux驅動的寫法的。當對于不熟悉設備樹的小伙伴,寫起來比較棘手 |
使用 i2c-tools,并通過腳本或者應用程序編寫設備驅動(簡單粗暴) | 此方法是將設備驅動丟到用戶態中,對于一些的設備除了I2C通信還有一些引腳也要控制的,此方法寫起來將非常痛苦 |
直接操作i2c總線驅動。(簡單粗暴) | 此方法是將設備驅動丟到用戶態中,對于一些的設備除了I2C通信還有一些引腳也要控制的,此方法寫起來將非常痛苦。他將會操作多個文件 |
- 上面的做法都有些困難及弊端存在,經過摸索了一遍Linux的I2C驅動框架,我發現可以很精簡的寫一個I2C設備的設備驅動。而且是放在內核態中,這樣處理一下GPIO或者中斷什么的都很方便。
投機取巧的I2C驅動
I2C設備驅動說明
- 投機取巧的I2C驅動是參考I2C總線驅動代碼實現的。
- 投機取巧的I2C驅動不需要設備樹,這也讓一些不熟悉設備樹的小伙伴能編寫一個設備驅動。
- 投機取巧的I2C驅動精簡,方便理解。
分析I2C總線驅動說明
-
I2C總線驅動的代碼在linux的源碼中--i2c-dev.c中。
-
在代碼中可以看到他提供一套文件操作接口,open,read,write,close接口。實際在上面描述的直接操作i2c總線驅動的方法,最終就是調用到這里。
-
通過整個源碼的分析,我們主要看看open和ioctl接口。其中:
-
open接口,代碼分析:通過inode獲取設備子設備號,根據子設備號獲取I2C適配器。然后申請一個從設備對象。并將I2C適配器句柄映射到從設備對象中。
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接口(只提取有用信息): 獲取從設備對象句柄,然后將用戶態傳輸的內容傳輸到i2cdev_ioctl_rdwr()接口。i2cdev_ioctl_rdwr()接口是i2c總線驅動對從設備操作的進一步封裝,我們進一步看一下這個函數。
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接口:通過接口可以看出,從用戶態拷貝數據,然后通過i2c_transfer接口進入從設備數據讀寫,然后判斷標志是否讀操作,如果為讀操作,將i2c_transfer接口接收回來的數據拷貝到用戶態。
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;
}
投機取巧的I2C驅動寫法
- 通過i2c總線驅動的源碼分析,實際我們的設備驅動可以通過這種模仿這個總線驅動來寫。
- 代碼模板如下:
#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為設備掛在的i2c總線的子設備號
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");
- 運行結果
Registesuccess,majornumberis240
i2creaddata:0x67!!
ricei2ckoinit!!
總結
- 通過投機取巧的方法,不需要設備樹的存在,就可以在內核態中編寫設備驅動,而且很靈活。
- 雖然這是一種可以讓我們快速開發驅動的方法,但是還是建議大家要去了解框架的邏輯。這樣不僅對自己的編碼能力,以及開發很有幫助。
- 希望本篇文章能夠幫助到大家。
審核編輯 黃昊宇
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
驅動
+關注
關注
12文章
1851瀏覽量
85642 -
Linux
+關注
關注
87文章
11345瀏覽量
210399 -
IIC
+關注
關注
11文章
302瀏覽量
38504 -
驅動開發
+關注
關注
0文章
130瀏覽量
12111
發布評論請先 登錄
相關推薦
怎么投機取巧移植RT-Thread到國產MCU上
KEIL5,IAR。串口助手使用的是SecureCRT。BSP基礎工程其實移植RT-THREAD到一些比較通用的內核還是比較方便的,因為可以投機取巧。那接下來告訴你怎么投機取巧移植RT-Thread到國產
發表于 06-09 10:57
告訴你怎么投機取巧移植RT-Thread到HC32L136芯片上
KEIL5,IAR。串口助手使用的是SecureCRT。BSP基礎工程其實移植RT-THREAD到一些比較通用的內核還是比較方便的,因為可以投機取巧。那接下來告訴你怎么投機取巧移植RT-Thread到國產
發表于 09-23 15:51
評論