前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux驱动开发的IIC设备驱动的投机取巧

Linux驱动开发的IIC设备驱动的投机取巧

作者头像
Rice加饭
发布2022-05-10 18:25:18
1.8K0
发布2022-05-10 18:25:18
举报
文章被收录于专栏:Rice嵌入式

前言

  • Linux的IIC驱动想必大家都耳熟能详,网上也有很多相关的教程。
  • 网上的教程总结,比如:

方法

问题描述

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适配器句柄映射到从设备对象中。
代码语言:javascript
复制
static int i2cdev_open(struct inode *inode, struct file *file)
{
 unsigned int minor = iminor(inode);
 struct i2c_client *client;
 struct i2c_adapter *adap;

 adap = i2c_get_adapter(minor);
 if (!adap)
  return -ENODEV;

 /* This creates an anonymous i2c_client, which may later be
  * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
  *
  * This client is ** NEVER REGISTERED ** with the driver model
  * or I2C core code!!  It just holds private copies of addressing
  * information and maybe a PEC flag.
  */
 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;

 return 0;
}

  • ioctl接口(只提取有用信息): 获取从设备对象句柄,然后将用户态传输的内容传输到i2cdev_ioctl_rdwr()接口。i2cdev_ioctl_rdwr()接口是i2c总线驱动对从设备操作的进一步封装,我们进一步看一下这个函数。
代码语言:javascript
复制
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
 struct i2c_client *client = file->private_data;
 unsigned long funcs;

    ......

 switch (cmd) {
    .....

 case I2C_RDWR:
  return i2cdev_ioctl_rdwr(client, arg);

    ......
 }
 return 0;
}
  • i2cdev_ioctl_rdwr接口:通过接口可以看出,从用户态拷贝数据,然后通过i2c_transfer接口进入从设备数据读写,然后判断标志是否读操作,如果为读操作,将i2c_transfer接口接收回来的数据拷贝到用户态。
代码语言:javascript
复制
static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
  unsigned long arg)
{
 struct i2c_rdwr_ioctl_data rdwr_arg;
 struct i2c_msg *rdwr_pa;
 u8 __user **data_ptrs;
 int i, res;

 if (copy_from_user(&rdwr_arg,
      (struct i2c_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);
 }
    ......

 return res;
}
投机取巧的I2C驱动写法
  • 通过i2c总线驱动的源码分析,实际我们的设备驱动可以通过这种模仿这个总线驱动来写。
  • 代码模板如下:
代码语言:javascript
复制
#include "rice_i2c.h"

#define CLASS_NAME  "rice_i2c"
#define DEVICE_NAME "rice_i2c"

typedef struct {
    int major_number;
    struct device *device;
    struct class *class;
    struct i2c_client *client;
} Rice_Driver;

Rice_Driver rice_drv;

static int i2c_test(void)
{
    struct i2c_msg i2c_msg[2] = {0};
    uint8_t reg_addr = 0x75;
    uint8_t buff[2] = {0};

    i2c_msg[0].addr  = 0x69;
    i2c_msg[0].flags = 0;
    i2c_msg[0].len   = 1;
    i2c_msg[0].buf   = (uint8_t *)&reg_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 "i2c read data: 0x%02x!!\n", buff[0]);
}

static int __init rice_i2c_init(void) {
    struct i2c_client *client;
 struct i2c_adapter *adap;

    rice_drv.major_number = register_chrdev(0, DEVICE_NAME, NULL);

    if (rice_drv.major_number < 0) {
        printk(KERN_ALERT "Register fail!!\n");
        return rice_drv.major_number;
    }

    printk(KERN_ALERT "Registe success, major number is %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);
        return PTR_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);
        return PTR_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 "rice i2c ko init!!\n");

    return 0;
}

static void __exit rice_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 "rice i2c ko exit!!\n");
}

module_init(rice_i2c_init);
module_exit(rice_i2c_exit);
MODULE_AUTHOR("RieChen");
MODULE_LICENSE("GPL");

  • 运行结果
代码语言:javascript
复制
Registe success, major number is 240
i2c read data: 0x67!!
rice i2c ko init!!

总结

  • 通过投机取巧的方法,不需要设备树的存在,就可以在内核态中编写设备驱动,而且很灵活。
  • 虽然这是一种可以让我们快速开发驱动的方法,但是还是建议大家要去了解框架的逻辑。这样不仅对自己的编码能力,以及开发很有帮助。
  • 希望本篇文章能够帮助到大家。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 投机取巧的I2C驱动
    • I2C设备驱动说明
      • 分析I2C总线驱动说明
        • 投机取巧的I2C驱动写法
        • 总结
        相关产品与服务
        腾讯云代码分析
        腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档