转:在内核里写i2c client 驱动的两种方式

2023-11-19

原文位置:https://www.cnblogs.com/simonshi/archive/2011/02/24/1963426.html

在内核里写i2c client 驱动的两种方式

前文介 绍了利用/dev/i2c-0在应用层完成对i2c设备的操作,但很多时候我们还是习惯为i2c设备在内核层编写驱动程序。目前内核支持两种编写i2c驱 动程序的方式。下面分别介绍这两种方式的实现。这里分别称这两种方式为“Adapter方式(LEGACY)”和“Probe方式(new style)”。

(1) Adapter方式(LEGACY)

(下面的实例代码是在2.6.27内核的pca953x.c基础上修改的,原始代码采用的是本文将要讨论的第2种方式,即Probe方式)

●    构建i2c_driver

static struct i2c_driver pca953x_driver = {
                .driver = {
                                    .name= "pca953x", //名称
                                },
                .id= ID_PCA9555,//id号
                .attach_adapter= pca953x_attach_adapter, //调用适配器连接设备
                .detach_client= pca953x_detach_client,//让设备脱离适配器
        };

●    注册i2c_driver

static int __init pca953x_init(void)
        {
                return i2c_add_driver(&pca953x_driver);
        }
        module_init(pca953x_init);

●    attach_adapter动作

执行i2c_add_driver(&pca953x_driver)后会,如果内核中已经注册了i2c适配器,则顺序调用这些适配器来连接我们的i2c设备。此过程是通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下:

static int pca953x_attach_adapter(struct i2c_adapter *adapter)
        {
                return i2c_probe(adapter, &addr_data, pca953x_detect);
                /*
                adapter:适配器
                addr_data:地址信息
                pca953x_detect:探测到设备后调用的函数
                */
        }

地址信息addr_data是由下面代码指定的。
        /* Addresses to scan */
        static unsigned short normal_i2c[] = {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END};
        I2C_CLIENT_INSMOD;

注意:normal_i2c里的地址必须是你i2c芯片的地址。否则将无法正确探测到设备。而I2C_ CLIENT_INSMOD是一个宏,它会利用normal_i2c构建addr_data。

●    构建i2c_client,并注册字符设备驱动

i2c_probe在探测到目标设备后,后调用pca953x_detect,并把当时的探测地址address作为参数传入。

static int pca953x_detect(struct i2c_adapter *adapter, int address, int kind)
        {
                struct i2c_client *new_client;
                struct pca953x_chip *chip; //设备结构体
                int err = 0,result;
                dev_t pca953x_dev=MKDEV(pca953x_major,0);//构建设备号,根据具体情况设定,这里我只考虑了normal_i2c中只有一个地址匹配的情况。
                if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_WORD_DATA))//判定适配器能力
                goto exit;
                if (!(chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL))) {
                        err = -ENOMEM;
                        goto exit;
                }
                /****构建i2c-client****/
                chip->client=kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
                new_client = chip->client;
                i2c_set_clientdata(new_client, chip);
                new_client->addr = address;
                new_client->adapter = adapter;
                new_client->driver = &pca953x_driver;
                new_client->flags = 0;
                strlcpy(new_client->name, "pca953x", I2C_NAME_SIZE);
                if ((err = i2c_attach_client(new_client)))//注册i2c_client
                goto exit_kfree;
                if (err)
                goto exit_detach;
                if(pca953x_major)
                {
                        result=register_chrdev_region(pca953x_dev,1,"pca953x");
                }
                else{
                        result=alloc_chrdev_region(&pca953x_dev,0,1,"pca953x");
                        pca953x_major=MAJOR(pca953x_dev);
                }
                if (result < 0) {
                        printk(KERN_NOTICE "Unable to get pca953x region, error %d\n", result);
                        return result;
                }
                pca953x_setup_cdev(chip,0); //注册字符设备,此处不详解
                return 0;
                exit_detach:
                i2c_detach_client(new_client);
        exit_kfree:
                kfree(chip);
        exit:
                return err;
        }

i2c_check_functionality用来判定设配器的能力,这一点非常重要。你也可以直接查看对应设配器的能力,如

static const struct i2c_algorithm smbus_algorithm = {
                .smbus_xfer= i801_access,
                .functionality= i801_func,
        };
        static u32 i801_func(struct i2c_adapter *adapter)
        {
                        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
                    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
                I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
                                | (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0);
        }

●    字符驱动的具体实现

struct file_operations pca953x_fops = {
                .owner = THIS_MODULE,
                .ioctl= pca953x_ioctl, 
                .open= pca953x_open, 
                .release =pca953x_release, 
        };

字符设备驱动本身没有什么好说的,这里主要想说一下,如何在驱动中调用i2c设配器帮我们完成数据传输。

目前设配器主要支持两种传输方法:smbus_xfer和master_xfer。一般来说,如果设配器支持了master_xfer那么它也可以模拟支持smbus的传输。但如果只实现smbus_xfer,则不支持一些i2c的传输。

int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
        int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
                                                                                unsigned short flags, char read_write,
                                                                u8 command, int size, union i2c_smbus_data * data);

master_xfer中的参数设置,和前面的用户空间编程一致。现在只是要在驱动中构建相关的参数然后调用i2c_transfer来完成传输既可。

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)

smbus_xfer中的参数设置及调用方法如下:

static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
        {
                int ret;
                ret = i2c_smbus_write_word_data(chip->client, reg << 1, val);
                if (ret < 0) {
                                dev_err(&chip->client->dev, "failed writing register\n");
                                        return -EIO;
                                }
                return 0;
        }

上面函数完成向芯片的地址为reg的寄存器写一个16bit的数据。i2c_smbus_write_word_data的实现如下:

s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
        {
                union i2c_smbus_data data;
                data.word = value;
                return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
                                                                        I2C_SMBUS_WRITE,command,
                                                                        I2C_SMBUS_WORD_DATA,&data);
        }

从中可以看出smbus传输一个16位数据的方法。其它操作如:字符写、字符读、字读、块操作等,可以参考内核的i2c-core.c中提供的方法。

●    注销i2c_driver

static void __exit pca953x_exit(void)
        {
                i2c_del_driver(&pca953x_driver);
        }
        module_exit(pca953x_exit);

●    detach_client动作

顺序调用内核中注册的适配器来断开我们注册过的i2c设备。此过程通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下:

static int pca953x_detach_client(struct i2c_client *client)
        {
                int err;
                struct pca953x_chip *data;
                if ((err = i2c_detach_client(client)))//断开i2c_client
                return err;
                data=i2c_get_clientdata(client);
                cdev_del(&(data->cdev));
                unregister_chrdev_region(MKDEV(pca953x_major, 0), 1);
                kfree(data->client);
                kfree(data);
                return 0;
        }

(2) Probe方式(new style)

●    构建i2c_driver

和LEGACY方式一样,也需要构建i2c_driver,但是内容有所不同。

static struct i2c_driver pca953x_driver = {
                .driver = {
                        .name= "pca953x",
                        },
                        .probe= pca953x_probe, //当有i2c_client和i2c_driver匹配时调用
                        .remove= pca953x_remove,//注销时调用
                        .id_table= pca953x_id,//匹配规则
        };

●    注册i2c_driver

static int __init pca953x_init(void)
        {
                return i2c_add_driver(&pca953x_driver);
        }
        module_init(pca953x_init);

在注册i2c_driver的过程中,是将driver注册到了i2c_bus_type的总线上。此总线的匹配规则是:

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                                                                                                const struct i2c_client *client)
        {
                while (id->name[0]) {
                        if (strcmp(client->name, id->name) == 0)
                                return id;
                        id++;
                }
                return NULL;
        }

可以看出是利用i2c_client的名称和id_table中的名称做匹配的。本驱动中的id_table为

static const struct i2c_device_id pca953x_id[] = {
                { "pca9534", 8, },
                { "pca9535", 16, },
                { "pca9536", 4, },
                { "pca9537", 4, },
                { "pca9538", 8, },
                { "pca9539", 16, },
                { "pca9554", 8, },
                { "pca9555", 16, },
                { "pca9557", 8, },
                { "max7310", 8, },
                { }
        };

看到现在我们应该会有这样的疑问,在Adapter模式中,i2c_client是我们自己构造出来的,而现在的i2c_client是从哪来的呢?看看下面的解释

●    注册i2c_board_info

对于Probe模式,通常在平台代码中要完成i2c_board_info的注册。方法如下:

/arch/arm/mach-xxxxx/mach-xxxxx.c

static struct i2c_board_info __initdata test_i2c_devices[] = {
                {
                        I2C_BOARD_INFO("pca9555", 0x27),//pca9555为芯片名称,0x27为芯片地址
                        .platform_data = &pca9555_data,
                }, {
                        I2C_BOARD_INFO("mt9v022", 0x48),
                        .platform_data = &iclink[0], /* With extender */
                }, {
                        I2C_BOARD_INFO("mt9m001", 0x5d),
                        .platform_data = &iclink[0], /* With extender */
                },
        };
        i2c_register_board_info(0, test_i2c_devices,ARRAY_SIZE(test_i2c_devices)); //注册

i2c_client就是在注册过程中构建的。但有一点需要注意的是i2c_register_board_info并没有EXPORT_SYMBOL给模块使用。

●    字符驱动注册

在Probe方式下,添加字符驱动的位置在pca953x_probe中。

static int __devinit pca953x_probe(struct i2c_client *client,const struct i2c_device_id *id)
        {
                        ……
                        /****字符设备驱动注册位置****/
                        ……
                        return 0;
        }

●    注销i2c_driver

static void __exit pca953x_exit(void)
        {
                i2c_del_driver(&pca953x_driver);
        }
        module_exit(pca953x_exit);

●    注销字符设备驱动

在Probe方式下,注销字符驱动的位置在pca953x_remove中。

static int __devinit pca953x_remove (struct i2c_client *client)
        {
                ……
                /****字符设备驱动注销的位置****/
                ……
                return 0;
        }

●    I2C设备的数据交互方法(即:调用适配器操作设备的方法)和Adapter方式下相同。

 

 

个人认为,在这两种方式之外,对于probe方式,Linux已经进行了一种新的方式,在3.x内核中将i2c_board_info替换为dts配置,对module_init 和 module_exit 等操作替换为了 module_i2c_driver 操作,具体可百度下关于 module_i2c_driver 的解析。这种方式的实现后面有时间的时候再来补充。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

转:在内核里写i2c client 驱动的两种方式 的相关文章

  • 【ts】使用ts的vue项目必须安装的插件和要进行的配置(一般使用搭建vue的时候脚手架会帮忙弄好,但是还是要知道项目里这些代码都是什么)

    使用ts的项目 是需要安装ts loader来处理 ts文件的 例如babel loader是用于处理js文件 而ts就是ts loader 先确保项目是否安装了webpack和typescript npm install webpack
  • 前端面试话术集锦第 14 篇:高频考点(React常考基础知识点)

    这是记录前端面试的话术集锦第十四篇博文 高频考点 React常考基础知识点 我会不断更新该博文 1 生命周期 在V16版本中引入了Fiber机制 这个机制一定程度上的影响了部分生命周期的调用 并且也引入了新的2个API来解决问题 在之前的版
  • 深度学习用streamlit做网页demo

    CSDN话题挑战赛第2期 参赛话题 学习笔记 深度学习 图像生成领域许多工作都会做网页demo 技术与应用接轨 让用户体验模型能力 之前试过用轻量级Web应用框架Flask将深度学习模型部署到微信小程序端 pytorch模型部署小程序 三思
  • 智能机器人编程有必要学吗?

    机器人编程是由各种传统的学科构成的 例如 数学 物理 化学 结构 建筑 地理 生物 能源 信息技术等 学习机器人可以培养孩子的各种能力 包括动手 想象 创造 观察 分析 判断 归纳 理解 决策 组织 实验 计划性 条理性等 如果你正为这些纷
  • 31种选择器的应用

    选择器 selector 是CSS中很重要的概念 所有HTML语言中的标记都是通过不同的CSS选择器进行控制的 用户只需要通过选择器对不同的HTML标签进行控制 并赋予各种样式声明 即可实现各种效果 1 1 margin 0 padding
  • 2021.1.18

    标题 标题 标题 标题 标题 标题 引用 1 代码引用 for i in range 100 print hello world 三个反引号 语言类别插入代码框 2 句中引用 ban掉菲奥娜 3 单行引用 亚索示意敌人已不见踪影 亚索已经0
  • 【9】Docker的迁移与备份

    1 容器保存为镜像 实际工作中经常会使用到 docker commit 容器名称 保存为新镜像的名称 不能和已有的镜像名称重复 我们可以通过以下的命令将容器保存为镜像 docker commit redis myredis 根据新保存的 镜
  • ping

    为什么ping不通 1 ping不通也有好几种返回信息的 Ping不成功的返回信息有 Request Timed Out Destination Net Unreachable 和 Ba d IP address 还有 Source que
  • 3个超好用的图片压缩软件,超高压缩比,保持图片清晰度

    分享3个好用的图片压缩工具 不仅可以大大压缩图片大小 还能保持图片分辨率 压缩后的图片画质也能保持清晰 1 一键抠图 一个智能抠图在线网站 支持人像抠图 物品抠图和证件照之外 除此之外 还有图片压缩 图片格式转换 图片编辑 照片修复等功能
  • ValueError: embedded null character

    问题 报错 ValueError embedded null character 原因 路径中包含 0 被认为是转义字符 不会自动识别为路径 导致报错 需注意含 0 t 等特殊转义字符的路径 解决方案 路径改为 1 D 00pythonPr
  • 电源滤波电路的设计

    一 电源滤波技术 常用的滤波措施有 去耦电容 电感 磁珠等 常用的滤波场景有 电源滤波 接口滤波等 在进行PCB设计时 滤波器件的摆放位置相当关键 对于电容类去耦的滤波原则是靠近滤波区域位置放置最佳 在滤波电路的EMC设计中 滤波的作用基本
  • 电磁场仿真工具们

    首先说明两点 1 不同的商用电磁场仿真工具可能采用的算法不同 但对于偏上电感等常见结构 在各仿真器设置正确的情况下 各仿真工具得到的结果应该相似 如果对仿真结果有疑问 最简单的验证方法是 对同一模型使用不同原理的两种电磁场仿真工具进行仿真
  • vue2使用 tinymce富文本编辑器-图片上传、粘贴图片上传致服务器

    1 安装tinymce富文本编辑器插件 npm i tinymce npm i tinymce tinymce vue 2 创建Editor js文件 封装组件 以便使用
  • c1048: [编程入门]自定义函数之字符串拷贝

    题目描述 有一字符串 包含n个字符 写一函数 将此字符串中从第m个字符开始的全部字符复制成为另一个字符串 输入 数字n 一行字符串 数字m 输出 从m开始的子串 样例输入复制 6 abcdef 3 样例输出复制 cdef 思路 两种方法 一
  • Kafka : KafkaProducer Closing the kafka producer with timeoutMillis

    1 美图 2 背景 一段kafka写入程序 不晓得为啥突然发现很多奇怪的日志 kafka 多线程发送数据 然后在本地是可以的 在服务器上是偶现的 我写了一个本地程序多线程生产数据 发现是没有问题的 Test public void mult
  • mysql“Access denied for user 'root'@'IP地址

    mysql Access denied for user root IP地址 2013 12 27 17 29 1308人阅读 评论 0 收藏 举报 分类 面试 经历 42 版权声明 本文为博主原创文章 未经博主允许不得转载 xshell连
  • kvm环境创建虚拟机

    使用libvirt的xml配置文件创建虚拟机 kvm支持的镜像很多 常用的是原始镜像 img 还有支持动态大小扩张的qocw2格式 首选 更优的选择是系统盘如C盘用img格式 数据盘用qcow2格式以减少服务器磁盘闲置空间 本文仅记录如何用
  • 微信H5如何关闭浏览器(如何监听手机的物理返回键)

    一 背景 背景是这样的 该项目进入h5时会通过 location replace xxx 或 location href xxx 跳转到某个地址 该地址会请求获得微信 openId 获取成功后再重定向到h5首页 那么问题来了 重定向会在微信
  • 《影响力》-社会心理学入门书

    这本书评价如何 豆瓣评分8 6 大多数人认可度还是很高的 查理芒格 穷查理宝典 的作者 觉得作者在 影响力 主题上 远超其他科学家 因此赠送了一股伯克希尔的A级股票 据说价值22w美元 财富 杂志推荐75本商业必读之一 我个人觉得 这本书其
  • 使用R语言中的survival包进行生存分析是一种常见的统计方法

    使用R语言中的survival包进行生存分析是一种常见的统计方法 在生存分析中 我们经常需要创建一个生存对象来存储事件发生时间和事件状态 在本文中 我们将介绍如何使用survival包中的Surv函数来创建生存对象 并解读其结果 Surv函

随机推荐

  • 代码随想录算法训练营第十九天

    动态规划系列5 6 7 8 377 组合总和 未看解答自己编写的青春版 重点 代码随想录的代码 我的代码 当天晚上理解后自己编写 求排列数的题 用二维DP过不了 自己捋逻辑的话 也是可以觉得有漏洞 但是怎么修改 一下子还没思路 包括后面的
  • GPRS公网通讯

    GPRS应用中TCP IP PPP基本概念 的原理与此差不多 实际上GPRS DTU上实现的是协议栈是TCP IP Over PPP 我们在使用时必须要有一些相应的概念 因此在下面我们就与SARO 3130P使用相关的一些事项作一些简要的说
  • gerrit提交出现remote rejected change closed错误

    看了这边文章 原因就像提示信息所说3203已经关闭了 change id找不到了 分析了原因 我的问题是因为之前的一次提交 3203 anandoned掉了 所以找不到这个change id 将这次提交重新恢复后 push成功
  • 区块链技术实战学习路线图

    请大家前往深入浅出区块链主站 获取最新内容 本章的文章越来越多 本文是一个索引帖 方便找到自己感兴趣的文章 你也可以使用左侧的分类 标签及搜索功能 有新文章时会更新本文 建议大家加入收藏夹中 如果你觉得本站不错 欢迎你转发给朋友 引言 给迷
  • 编写简单的linux shell脚本

    1 touch hello sh 2 vi hello sh 键入i 插入 bin sh echo hello world 键入 esc wq 3 chmod 700 hello sh 4 执行 hello sh
  • linux 上生成图片的问题 (awt)

    1 启动xwindow 执行命令 xhost local 2 参考下面资料 原因 Linux无图形支持环境配置 解决 在catalina sh 中加入 Djava awt headless true 或者在 bash profile中增加
  • Redis持久化AOF

    目录 1 AOF简介 2 AOF持久化流程 3 AOF默认不开启 4 AOF和RDB同时开启 redis听谁的 5 AOF启动 修复 恢复 6 AOF同步频率设置 7 Rewrite压缩 7 1 是什么 7 2 重写原理 如何实现重写 8
  • 微信小程序:自动生成打卡海报

    文章目录 1 前言 2 界面展示 3 部分代码展示以及原理解释 4 结语 完整项目下载 下载链接 1 前言 在当前的背单词小程序开发中 为满足用户学习完成后的展示需求 计划引入自动生成打卡海报功能 以提升用户参与度与推广效果 除了基本的海报
  • Window XP驱动开发(十三) 芯片功能驱动端 (代码实现,针对USB2.0 芯片CY7C68013A)

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要源码的可以与我联系 针对USB2 0 芯片CY7C68013A FPGA实现的高速传输应用来写XP下的USB驱动程序 说
  • 医学图像配准工具Elastix的配置和入门

    一 Elastix介绍 Elastix是一个基于ITK开发的处理医学图像配准问题的工具 Elastix提供了很方便的命令行使用方式以供使用者进行配准应用 同时Elastix是开源的 并且采用模块式构成 可以根据源代码进行开发 或者添加新的模
  • 在LinuxBridge/OVS中使用VxLAN组网以及创建VTEP

    原文来自于 https blog lofyer org E5 9C A8linux bridge ovs E4 B8 AD E4 BD BF E7 94 A8vxlan E7 BB 84 E7 BD 91 E4 BB A5 E5 8F 8A
  • 【满分】【华为OD机试真题2023 JS】箱子之形摆放

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 箱子之形摆放 知识点数组 时间限制 1s 空间限制 128MB 限定语言 不限 题目描述 有一批箱子 形式为字符串 设为str 要求将这批箱子按从上到下以之形的顺序摆放在宽度为n
  • js的事件执行机制(Event loop)

    同步任务 执行主线程上排队执行的任务 只有前一个任务执行完毕 下一个任务才会开始执行 异步任务 不进入主线程 而进入 任务队列 task queue 的任务 只有 任务队列 通知主线程 某个异步任务可以执行了 该任务才会进入主线程执行 事件
  • docker安装nginx太多坑了,果断放弃

    以下是我本人的个人看法 如有不对可在评论区讨论交流 1 listen的端口受限于docker p的参数 一个nginx容器conf文件只能listen同一个端口 2 修改配置文件麻烦 还有docker exec进入到容器内部进行操作 当然
  • 医疗保健软件必备指南

    对许多人来说 软件可能是一种奢侈品 只会给生活在 21 世纪的人们带来一些额外好处 但有时 软件可能是救命稻草 起着生死攸关的作用 根据医疗行业的部分统计数据 我们清醒地发现 美国平均每年约有 25 万至 40 万患者死于本可预防的医疗差错
  • Qt队列的使用

    一 queue 队列 队列是一种先进先出的数据结构 是一个模板类 队列和栈是一种数据逻辑概念 即数据能进行的操作 主要区别是 队列先进先出 First In First Out 栈后进先出 链表和顺序表是一种数据存放方式 主要区别是 链表有
  • 向日葵远程连接Ubuntu出现 “连接中断“ 的解决方法

    向日葵远程连接Ubuntu出现 连接中断 的解决方法 https www cnblogs com wangling1820 p 13448397 html 方法一 参考博客1 https blog csdn net wzf20162016
  • styled-components 的用法

    用于给标签或组件添加样式 给标签或组件添加样式 import styled from styled components styled button 给button标签添加样式 const Button styled button back
  • opencv中归一化函数cv2.normalize()的原理讲解

    本篇文章参考博客 https blog csdn net kuweicai article details 78988886 功能 归一化函数 参数 Python cv2 normalize src dst alpha beta norm
  • 转:在内核里写i2c client 驱动的两种方式

    原文位置 https www cnblogs com simonshi archive 2011 02 24 1963426 html 在内核里写i2c client 驱动的两种方式 前文介 绍了利用 dev i2c 0在应用层完成对i2c