前言
方法 | 问题描述 |
---|---|
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_number< 0){
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!!
总结
- 通过投机取巧的方法,不需要设备树的存在,就可以在内核态中编写设备驱动,而且很灵活。
- 虽然这是一种可以让我们快速开发驱动的方法,但是还是建议大家要去了解框架的逻辑。这样不仅对自己的编码能力,以及开发很有帮助。
- 希望本篇文章能够帮助到大家。
审核编辑 黄昊宇
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
驱动
+关注
关注
11文章
1717浏览量
84340 -
Linux
+关注
关注
87文章
10990浏览量
206733 -
IIC
+关注
关注
11文章
285浏览量
37800 -
驱动开发
+关注
关注
0文章
129浏览量
12010
发布评论请先 登录
相关推荐
怎么投机取巧移植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
评论