首页 > 其他分享 >【驱动】I2C驱动分析(六)-I2C驱动模板

【驱动】I2C驱动分析(六)-I2C驱动模板

时间:2024-01-18 23:02:13浏览次数:38  
标签:i2c return driver static 驱动 I2C hello 模板

前言

Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片、音视频采集芯片、音视频输出芯片、EEROM芯片、AD/DA转换芯片等等。下面我们看下如何写一个基本的I2C驱动。

内核I2C驱动框架

I2C driver端

I2C驱动初始化及模块声明

驱动初始化主要是调用内核的i2c_add_driver向内核注册hello_drv结构体。hello_drv结构体中描述了驱动的名称。exit函数会调用i2c_del_driver删除注册的hello_drv

/* 驱动入口函数 */
static int hello_i2c_drv_init(void)
{
    int ret = 0;
    printk(KERN_ALERT  "hello i2c driver init!!!\n");
    ret  = i2c_add_driver(&hello_i2c_driver);
    if(ret){
        printk(KERN_ALERT "add driver failed!!!\n");
        return -ENODEV;
    }
    return 0;
}


 /* 驱动出口函数 */
static void hello_i2c_drv_exit(void)
{ 
	i2c_del_driver(&hello_i2c_driver);
    return ;
}

module_init(hello_i2c_drv_init);
module_exit(hello_i2c_drv_exit);
 
MODULE_DESCRIPTION("hello i2c driver");
MODULE_AUTHOR("ZhongYi");
MODULE_LICENSE("GPL");

i2c_driver结构体

每一个I2C设备驱动,必须首先创造一个i2c_driver结构体对象,该结构体包含了I2C设备探测和注销的一些基本方法和信息。

static struct i2c_driver hello_i2c_driver =
      {
          .probe = hello_probe,
          .remove = hello_remove,
          .driver =
              {
                  .owner = THIS_MODULE,
                  .name = "hello_i2c",
                  .of_match_table = of_match_ptr(hello_i2c_of_match),
              },
          .id_table = hello_drv_id_table,
      };

name字段标识本驱动的名称,hello_probehello_remove字段为函数指针,这两个函数在I2C设备注册和注销的时候会自动调用,需要自己实现这两个函数。接下来的任务就是填充i2c_driver 结构体。

hello_drv_id_table

hello_drv_id_table表示以传统方式匹配设备和驱动程序。hello_i2c_of_match 表示以设备树方式匹配。现在大多数都是以设备树方式匹配。

/*传统方式匹配*/
static const struct i2c_device_id hello_drv_id_table[] = {
    {"hello_i2c",0},
    {},
};

/* 设备树匹配表 */
static const struct of_device_id hello_i2c_of_match[] = {
    {
        .compatible = "hello_i2c_driver",
    },
    {}
 
};

I2C驱动探测函数

probe函数会以CLASS_NAME创建一个class结构,这个动作将会在/sys/class目录创建一个名为CLASS_NAME的目录,接着以DEVICE_NAME为名,参考/sys/class/CLASS_NAME在/dev目录下创建一个设备:/dev/DEVICE_NAME*/

static int hello_probe(struct i2c_client *client,
			       const struct i2c_device_id *dev_id)
{

    printk(KERN_ALERT "addr = %x\n",client->addr);
    hello_client = client;
    major = register_chrdev(0,DEVICE_NAME,&file_oprts);
    if(major < 0 ){
        printk(KERN_ALERT "Register failed!!\r\n");
        return major;
    }
    printk(KERN_ALERT "Registe success,major number is %d\r\n",major);

    i2c_hello_cls = class_create(THIS_MODULE,CLASS_NAME);
    if(IS_ERR(i2c_hello_cls))
    {
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_hello_cls);
    }

    i2c_hello_dev = device_create(i2c_hello_cls,NULL,MKDEV(major,0),NULL,DEVICE_NAME);
    if(IS_ERR(i2c_hello_dev))
    {
        class_destroy(i2c_hello_cls);
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_hello_dev);
    }
    printk(KERN_ALERT "i2c_hello device init success!!\r\n");
}

hello_remove

当匹配关系不存在时(device或是driver被卸载),调用remove函数,remove函数是probe函数的反操作,将probe函数中申请的资源全部释放。

static int hello_remove(struct i2c_client *client)
{
 	printk(KERN_ALERT  "hello i2c remove!!!\n");
    device_destroy(i2c_hello_cls,MKDEV(major,0));
    class_unregister(i2c_hello_cls);
    class_destroy(i2c_hello_cls);
    unregister_chrdev(major,DEVICE_NAME);
    return 0;
}

I2C file_operations 结构体

file_operations 结构体描述的read,write等函数,对应于用户空间的readwrite。在用户态可以直接操作/dev/DEVICE_NAME*/ 节点来使用I2C驱动。

static struct file_operations file_oprts = 
{
    .open = i2c_hello_open,
    .read = i2c_hello_read,
    .write = i2c_hello_write,
    .release = i2c_hello_release,
};

I2C 设备数据收发处理流程

通过 i2c_smbus_read_byte_data 函数对I2C 设备寄存器进行读写操作。使用i2c_smbus_read_byte_data函数发送数据之前要先构建好 i2c_msg

static ssize_t i2c_hello_read(struct file *file,char *buf, size_t len,loff_t *offset)
{
    int cnt = 0;
    uint8_t reg = 0;
    uint8_t val = 0;
    copy_from_user(&reg,buf,1);
    val = i2c_smbus_read_byte_data(hello_client,reg);
    cnt = copy_to_user(&buf[1],&val,1);
    return 1;
}

static ssize_t i2c_hello_write(struct file *file,const char *buf,size_t len,loff_t *offset)
{
    uint8_t recv_msg[255] = {0};
    uint8_t reg = 0;
    int cnt = 0;
    cnt = copy_from_user(recv_msg,buf,len);
    reg = recv_msg[0];
    if(i2c_smbus_write_byte_data(hello_client,reg,recv_msg[1]) < 0){
        printk(KERN_ALERT  " write failed!!!\n");
        return -EIO;
    }
    return len;
}

完整代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

static int major;
static struct class *i2c_hello_cls;
static struct device *i2c_hello_dev;
static const char* CLASS_NAME = "I2C_HELLO_CLASS";
static const char* DEVICE_NAME = "I2C_HELLO_DEVICE";

static struct i2c_client *hello_client;

static int i2c_hello_open(struct inode *node, struct file *file)
{
    printk(KERN_ALERT "hello i2c open!\n");
    return 0;
}

static int i2c_hello_release(struct inode *node,struct file *file)
{
    printk(KERN_INFO "hello i2c release!!\n");
    
    return 0;
}
static ssize_t i2c_hello_read(struct file *file,char *buf, size_t len,loff_t *offset)
{
    int cnt = 0;
    uint8_t reg = 0;
    uint8_t val = 0;
    copy_from_user(&reg,buf,1);
    val = i2c_smbus_read_byte_data(hello_client,reg);
    cnt = copy_to_user(&buf[1],&val,1);
    return 1;
}

static ssize_t i2c_hello_write(struct file *file,const char *buf,size_t len,loff_t *offset)
{
    uint8_t recv_msg[255] = {0};
    uint8_t reg = 0;
    int cnt = 0;
    cnt = copy_from_user(recv_msg,buf,len);
    reg = recv_msg[0];
    if(i2c_smbus_write_byte_data(hello_client,reg,recv_msg[1]) < 0){
        printk(KERN_ALERT  " write failed!!!\n");
        return -EIO;
    }
    return len;
}

static struct file_operations file_oprts = 
{
    .open = i2c_hello_open,
    .read = i2c_hello_read,
    .write = i2c_hello_write,
    .release = i2c_hello_release,
};
static int hello_remove(struct i2c_client *client)
{
 	printk(KERN_ALERT  "hello i2c remove!!!\n");
    device_destroy(i2c_hello_cls,MKDEV(major,0));
    class_unregister(i2c_hello_cls);
    class_destroy(i2c_hello_cls);
    unregister_chrdev(major,DEVICE_NAME);
    return 0;
}

static int hello_probe(struct i2c_client *client,
			       const struct i2c_device_id *dev_id)
{

    printk(KERN_ALERT "addr = %x\n",client->addr);
    hello_client = client;
    major = register_chrdev(0,DEVICE_NAME,&file_oprts);
    if(major < 0 ){
        printk(KERN_ALERT "Register failed!!\r\n");
        return major;
    }
    printk(KERN_ALERT "Registe success,major number is %d\r\n",major);

    i2c_hello_cls = class_create(THIS_MODULE,CLASS_NAME);
    if(IS_ERR(i2c_hello_cls))
    {
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_hello_cls);
    }

    i2c_hello_dev = device_create(i2c_hello_cls,NULL,MKDEV(major,0),NULL,DEVICE_NAME);
    if(IS_ERR(i2c_hello_dev))
    {
        class_destroy(i2c_hello_cls);
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_hello_dev);
    }
    printk(KERN_ALERT "i2c_hello device init success!!\r\n");
}
/*传统方式匹配*/
static const struct i2c_device_id hello_drv_id_table[] = {
    {"hello_i2c",0},
    {},
};

/* 设备树匹配表 */
static const struct of_device_id hello_i2c_of_match[] = {
    {
        .compatible = "hello_i2c_driver",
    },
    {}
};

static struct i2c_driver hello_i2c_driver =
      {
          .probe = hello_probe,
          .remove = hello_remove,
          .driver =
              {
                  .owner = THIS_MODULE,
                  .name = "hello_i2c",
                  .of_match_table = of_match_ptr(hello_i2c_of_match),
              },
          .id_table = hello_drv_id_table,
      };

/* 驱动入口函数 */
static int hello_i2c_drv_init(void)
{
    int ret = 0;
    printk(KERN_ALERT  "hello i2c driver init!!!\n");
    ret  = i2c_add_driver(&hello_i2c_driver);
    if(ret){
        printk(KERN_ALERT "add driver failed!!!\n");
        return -ENODEV;
    }
    return 0;
}


 /* 驱动出口函数 */
static void hello_i2c_drv_exit(void)
{ 
	i2c_del_driver(&hello_i2c_driver);
    return ;
}

module_init(hello_i2c_drv_init);
module_exit(hello_i2c_drv_exit);
 
MODULE_DESCRIPTION("hello i2c driver");
MODULE_AUTHOR("ZhongYi");
MODULE_LICENSE("GPL");

device端

dev_init

dev_init调用i2c_get_adapter函数,用来获取指定适配器的函数,适配器一般指板上I2C控制器,实现i2c底层协议的字节收发,特殊情况下,用普通gpio模拟I2C也可作为适配器。i2c_new_device函数,用于创建一个新的I2C设备。函数传入参数adap表示要使用的适配器,&hello_board是一个指向设备描述结构体的指针,描述了要创建的设备的属性。

int dev_init(void)
{
    adap = i2c_get_adapter(2);
    if(IS_ERR(adap)){
        printk(KERN_ALERT  "I2c_get_adapter failed!!!\n");
        return -ENODEV;
    }
    client = i2c_new_device(adap,&hello_board);
    i2c_put_adapter(adap);
    if(!client){
    printk(KERN_ALERT  "Get new device failed!!!\n");
    return -ENODEV;
    }
    return 0;
}

I2C 设备信息描述

未使用设备树的时候,使用 i2c_board_info 结构体来描述一个具体的 I2C 设备。typeaddr 这两个成员变量是必须要设置的,一个是 I2C 设备的名字,一个是 I2C 设备的器件地址。当这个模块被加载时,i2c总线将使用hello_i2c名称匹配相应的drv。

#define I2C_BOARD_INFO(dev_type, dev_addr) \
 .type = dev_type, .addr = (dev_addr)

static struct i2c_board_info hello_board = {
    I2C_BOARD_INFO("hello_i2c",I2C_DEVICE_ADDR),
};

使用设备树的时候,通过在设备树中创建相应的节点就行了。

  &i2c1 {
    clock - frequency = <100000>;
    pinctrl - names = "default";
    pinctrl - 0 = <&pinctrl_i2c1>;
    status = "okay";

    mag3110 @0e {
      compatible = "hello_i2c_driver";
      reg = <0x0e>;
      position = <2>;
    };
  };

现在基本上都使用设备树

完整代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/regmap.h>

static struct i2c_adapter *adap;
static struct i2c_client  *client;
#define  I2C_DEVICE_ADDR   0x68

#define I2C_BOARD_INFO(dev_type, dev_addr) \
 .type = dev_type, .addr = (dev_addr)

static struct i2c_board_info hello_board = {
    I2C_BOARD_INFO("hello_i2c",I2C_DEVICE_ADDR),
};

int dev_init(void)
{
    adap = i2c_get_adapter(2);
    if(IS_ERR(adap)){
        printk(KERN_ALERT  "I2c_get_adapter failed!!!\n");
        return -ENODEV;
    }
    client = i2c_new_device(adap,&hello_board);
    i2c_put_adapter(adap);
    if(!client){
    printk(KERN_ALERT  "Get new device failed!!!\n");
    return -ENODEV;
    }
    return 0;
}

void dev_exit(void)
{
    i2c_unregister_device(client);
    return ;
}

https://blog.csdn.net/lgc1990/article/details/122665032

https://www.cnblogs.com/downey-blog/p/10493289.html

https://blog.51cto.com/ticktick/760020

https://blog.csdn.net/m0_64560763/article/details/127008302

https://www.cnblogs.com/kn-zheng/p/17579347.html

用户态代码

#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

static char buf[256] = {1};

int main(int argc,char *argv[])
{
    int ret = 0;
    uint8_t buf[2] = {0};
    char cmd[6] = {0};
    int reg_addr = 0;
    int value = 0;
    int fd = open("/dev/I2C_HELLO_DEVICE",O_RDWR);
    if(fd < 0)
    {
        perror("Open file failed!!!\r\n");
    }
    while(1){

        printf("Enter your cmd:<read/write> <reg_addr> <val> : \n");
        scanf("%s",cmd);
        scanf("%x",&reg_addr);
        scanf("%x",&value);
        printf("%s :%x :%x\n",cmd,reg_addr,value);
        if(0 == memcmp(cmd,"write",5)){
            buf[0] = reg_addr;
            buf[1] = value;
            int ret = write(fd,buf,2);
            if(ret < 0){
                perror("Failed to write!!\n");
            }else{
                printf("Write value %x to reg addr %x success\n",value,reg_addr);
            }
        }
        else if(0 == memcmp(cmd,"read",4)){
            buf[0] = reg_addr;
            ret = read(fd,buf,1);
            if(ret < 0){
                perror("Read failed!!\n");
            }else{
                printf("Read %x from addr %x\n",buf[1],reg_addr);
            }
            
        }
        else{
            printf("Unsupport cmd\n");
        }
        memset(cmd,0,sizeof(cmd));
    }
    close(fd);
    
    return 0;
}

模拟I2C驱动

模拟I2C驱动主要是配置dts,没什么可讲的。大家可以参考这篇文章:https://blog.csdn.net/landishu/article/details/118441943

本文参考

https://blog.csdn.net/ZHONGCAI0901/article/details/131167968

https://blog.csdn.net/chenyefei/article/details/51823851

.https://blog.csdn.net/ch122633/article/details/120144785

标签:i2c,return,driver,static,驱动,I2C,hello,模板
From: https://www.cnblogs.com/dongxb/p/17973625

相关文章

  • 【驱动】I2C驱动分析(一)-I2C驱协议简介
    什么是I²CI²C叫集成电路总线它是一种串行通信接口,具有双向两线同步串行总线,通常由两根线组成——SDA(串行数据线)和SCL(串行时钟线)和上拉电阻。它们用于需要许多不同部件(例如传感器、引脚、扩展和驱动程序)协同工作的项目,因为它们可以将多达128个设备连接到主板,同时保持清晰......
  • 世微AP2121太阳能草坪灯驱动芯片
    概述AP2121是一款专为太阳能LED草坪灯设计的专用集成电路。AP2121仅需一个外接电感即可组成太阳能照明装置。AP2121由开关型驱动电路、光控开关电路、过放保护电路、内部集成的肖特基二极管等电路组成。AP2121采用专利技术,使得欠压关断时LED灯无闪烁AP2121工......
  • IDEA设置注释模板
    进入设置File-->Setting2.在设置界面找到Editor下的LiveTemplates,点击JAVA新增一个LiveTemlate在下面的Templatetext中输入***$param$$return$*@authorbaorui*@date$date$$time$*/在Abbreviation中输入**点击按钮Editvariables进入参数配......
  • systemtap统计 探测linux驱动中某个函数的执行时间
    直接上脚本:#!/usr/bin/stapglobalstart,endprobemodule("your_driver").function("your_function"){start[tid()]=gettimeofday_ns()}probemodule("your_driver").function("your_function").return{end[tid()]=get......
  • 怎样打造品牌社区能驱动持续增长?
    社交渠道的崛起从根本上改变了品牌对品牌建设、产品开发和市场营销的思考,在这种环境下,建立长期的忠诚度既是品牌面临的最大挑战,也是最大机遇。本文将深度分析当前市场发展的新机遇,由此延伸出如何打造品牌社区驱动增长飞轮,并以四大等级辅助企业衡量自身品牌社区建设的成熟度,帮助企......
  • 算法设计与分析-算法模板(不定期更新)
    算法大纲基础算法:模拟,分治,递归,排序,概率,堆进阶算法:DP,贪心,图论,计算几何,FFT,字符串22级期末考试题型:诚信承诺题、选择题5道、归并思想(175/184)、排序的变形题(155/169)、贪心(170/185)、线性动态规划(77/136)、区间动态规划(33/61)、最短路变形(3/25)、多源多汇最大流(12/23),之后......
  • 基础算法(二)归并排序模板
    模板如下#include<iostream>usingnamespacestd;constintN=1000010;intq[N],tmp[N];voidmerge_sort(intq[],intl,intr){if(l>=r)return;intmid=(l+r)>>1;intk=0,i=l,j=mid+1;merge_sort(q,l,mid);merge_sort(q,......
  • 阿里云云原生助力安永创新驱动力实践探索
    云原生正在成为新质生产力变革的核心要素和企业创新的数字基础设施。2023年12月1日,由中国信通院举办的“2023云原生产业大会”在北京召开。在大会“阿里云云原生”专场,安永科技咨询合伙人王祺分享了对云原生市场的总览及趋势洞见,及安永基于阿里云容器服务、消息、微服务引擎......
  • 模板编程
    函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行的时候,将根据传递的实际参数决定其功能(运行期间的多态,动态多态)。C++提供两种模板机制:类模板和函数模板。注意这里T类型必须在使用模板的时候定义,而且可以有多个T......
  • 基于Minifilter的文件过滤驱动以及与应用层通讯
    前一段时间在做一个文件过滤系统,为了配合公司的产品使用,希望对指定目录禁止访问。一开始使用的是sfilter的框架,很多事情需要自己做,创建过滤驱动的控制设备,创建符号链接,设置IRP例程,设置FASTI/O例程,用这个框架做了一半,与应用层通讯比较麻烦,就又去学习了Minifilter框架,这个框架就非......