Linux驱动调试——使用DEVICE_ATTR实现cat、echo指令调试驱动

在平常做一些驱动调试的时候,每次都写应用去调试相对较麻烦,有一个非常便捷的操作方法就是使用device_attr,只需要执行shell指令例如echo和cat就可以看到效果,不需要再单独写一个测试demo

看网上很多博客在这一块的使用上写的都有些复杂了,都是纯写一个用DEVICE_ATTR的demo,很少结合具体驱动来配合,完成调试工作,所以决定自己也写一篇。

1.函数介绍

1.1 DEVICE_ATTR

DEVICE_ATTR宏定义在kernel/include/linux/device.h文件中。原型如下:

#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

作用:定义一个device_attribute结构体,之后device_create_file使用
_name:名称,在/sys/device/virtual中的名称,##name代表将name的字段直接替换到##_name处,所以假设_name为ap3216c_test,那这边定义的结构体名就是dev_attr_ap3216c_test
_mode:访问权限,与普通文件相同,可以使用0644也可以使用S_IWUSR|S_IRUSR这种格式代表用户可读可写
_show:显示函数,cat文件时调用此函数
_store:写函数,echo内容到文件时调用此函数

1.2 device_create_file

int device_create_file(struct device *dev, const struct device_attribute *attr)
{int error = 0;if (dev)error = sysfs_create_file(&dev->kobj, &attr->attr);return error;
}

作用:创建device属性文件
dev:设备指针,通过device_create创建
attr:device_attribute指针,就是通过DEVICE_ATTR宏生成的dev_attr_name指针

1.3 device_remove_file

void device_remove_file(struct device *dev, const struct device_attribute *attr)
{if (dev)sysfs_remove_file(&dev->kobj, &attr->attr);
}

作用:删除device属性文件

2.例程

这里我是结合了一个I2C设备驱动,对这个I2C驱动实现感兴趣的话可以看我另外一篇博客:I2C驱动编写
完成这个驱动后,之后便可以使用cat和echo来进行测试,而不再需要编写应用的测试demo。

使用主要分为四步
1)使用DEVICE_ATTR宏实现结构体

static DEVICE_ATTR(ap3216c_test, S_IWUSR|S_IRUSR, ap3216c_show, ap3216c_store);

2)实现show和store的函数

static ssize_t ap3216c_show(struct device *dev, struct device_attribute *attr,char *buf)
{u8 data[3] = {0};printk("ap3216c ap3216c_show");__ap3216c_read(&ap3216c_dev, data);memcpy(buf, data, sizeof(data) / sizeof(data[0]));return sizeof(data) / sizeof(data[0]);
}static ssize_t ap3216c_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{printk("ap3216c ap3216c_store, buf:%s, len:%d", buf, count);return count;
}

3)调用device_create_file创建device属性文件

if (device_create_file(ap3216c_dev.m_dev, &dev_attr_ap3216c_test) != 0) {printk("ap3216c device create failed\n");return -1;
}

4)驱动卸载时调用device_delete_file删除device属性文件

device_remove_file(ap3216c_dev.m_cdev,  &dev_attr_ap3216c_test);

完整代码如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#include <linux/device.h>/* AP3316C 寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR 数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR 数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS 数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS 数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS 数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS 数据高字节 */#define AP3216C_DATA_REG_NUM 6#define AP3216C_NAME "ap3216c"struct ap3216c_dev_t {dev_t m_devid;struct cdev m_cdev;     ///< 字符设备结构struct class *m_class;  ///< 类struct device *m_dev;   ///< 设备struct device_node *nd; ///< 设备节点int devid;              ///< 设备号int major;               ///< 主设备号struct i2c_client* client; ///<i2c设备
};struct ap3216c_dev_t ap3216c_dev = {0}; static ssize_t __ap3216c_read(struct ap3216c_dev_t *dev, char __user *data);static ssize_t ap3216c_show(struct device *dev, struct device_attribute *attr,char *buf)
{u8 data[3] = {0};printk("ap3216c ap3216c_show");__ap3216c_read(&ap3216c_dev, data);memcpy(buf, data, sizeof(data) / sizeof(data[0]));return sizeof(data) / sizeof(data[0]);
}static ssize_t ap3216c_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{printk("ap3216c ap3216c_store, buf:%s, len:%d", buf, count);return count;
}static DEVICE_ATTR(ap3216c_test, S_IWUSR|S_IRUSR, ap3216c_show, ap3216c_store);static int ap3216c_write_reg(struct ap3216c_dev_t *dev, u8 reg, u8 *data, u16 len)
{struct i2c_client* client = (struct i2c_client*)dev->client;struct i2c_msg msg;msg.addr = client->addr;            ///< 设备地址msg.flags = 0;                      ///< 写数据msg.buf[0] = reg;                   ///< 操作寄存器地址memcpy(&msg.buf[1], data, len);     ///< 写寄存器的数据msg.len = len + 1 ;                 ///< 1byte寄存器地址+data的长度    return i2c_transfer(client->adapter, &msg, 1);
}static int ap3216c_read_reg(struct ap3216c_dev_t *dev, u8 reg, u8 *data, u16 len)
{struct i2c_client* client = (struct i2c_client*)dev->client;struct i2c_msg msg[2];msg[0].addr = client->addr;             ///< 设备地址msg[0].flags = 0;                       ///< 写数据msg[0].buf = &reg;                    ///< 操作寄存器地址msg[0].len = 1 ;                        ///< 1byte寄存器地址 msg[1].addr = client->addr;            ///< 设备地址msg[1].flags = I2C_M_RD;               ///< 读数据msg[1].buf = data;                     ///< 读取数据缓冲区msg[1].len = len;                      ///< 读取数据长度 return i2c_transfer(client->adapter, &msg, 2);
}static int ap3216c_open(struct inode *node, struct file *file)
{u8 value = 0x00;printk("ap3216c driver open\n");file->private_data = (void *)&ap3216c_dev; ///< 设置私有数据ap3216c_write_reg(file->private_data, AP3216C_SYSTEMCONG, &value, 1);  ///< set sw resetmdelay(20); ///< 最少等待10msvalue = 0x03;ap3216c_write_reg(file->private_data, AP3216C_SYSTEMCONG, &value, 1);  ///< 设置为ALS and PS+IR模式printk("ap3216c driver open success\n");return 0;
}static int ap3216c_close(struct inode *node, struct file *file)
{u8 value = 0x00;printk("ap3216c driver close\n");file->private_data = (void *)&ap3216c_dev; ///< 设置私有数据ap3216c_write_reg(file->private_data, AP3216C_SYSTEMCONG, &value, 1);  ///< set sw resetprintk("ap3216c driver close success\n");return 0;
}static ssize_t __ap3216c_read(struct ap3216c_dev_t *dev, char __user *data)
{int i = 0;int ret = 0;u8 read_buf[AP3216C_DATA_REG_NUM] = {0};/// 循环将IR、ALS、PS中的寄存器数据读取for (i = 0; i < AP3216C_DATA_REG_NUM; i++) {ret = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i, read_buf, 6);printk("ap3216c driver read ret:%d, reg:%x, value:%d\n", ret, AP3216C_IRDATALOW + i, read_buf[i]);}if (read_buf[0] & 0x80) {data[0] = 0;printk("ap3216c driver read ir data error\n");} else {data[0] = (read_buf[1] << 2) | read_buf[0];printk("ap3216c driver read ir data success, data:%x\n", data[0]);}data[1] = (read_buf[3] << 8) | read_buf[2];printk("ap3216c driver read als data success, data:%x\n", data[1]);if (read_buf[4] & 0x80) {data[2] = 0;printk("ap3216c driver read ps data error\n");} else {data[2] = ((read_buf[5] & 0x3f) << 4) | (read_buf[4] & 0x0f);printk("ap3216c driver read ir data success, data:%x\n", data[0]);}printk("ap3216c driver read als data success, data:%x\n", data[1]);return 0;
}static ssize_t ap3216c_read(struct file *file, char __user *buf, size_t cnt, loff_t *off)
{u8 data[3] = {0};int ret = 0;printk("ap3216c driver read\n");file->private_data = (void *)&ap3216c_dev; ///< 设置私有数据__ap3216c_read(file->private_data, data);if (copy_to_user(buf, data, sizeof(data) / sizeof(data[0])) != 0) {return -1;}printk("ap3216c driver read sucess, ret:%d\n", ret);return ret;
}static struct file_operations ap3216c_fops = {.owner = THIS_MODULE,.open = ap3216c_open,.release = ap3216c_close,.read = ap3216c_read,
};int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{printk("ap3216c driver probe begin\n");///1.申请设备号if (ap3216c_dev.major) {ap3216c_dev.devid = MKDEV(ap3216c_dev.major, 0);register_chrdev_region(ap3216c_dev.devid, 1, AP3216C_NAME);} else {alloc_chrdev_region(&ap3216c_dev.devid, 0, 1, AP3216C_NAME);ap3216c_dev.major = MAJOR(ap3216c_dev.devid);}///2.cdev_init、cdev_addcdev_init(&ap3216c_dev.m_cdev, &ap3216c_fops);cdev_add(&ap3216c_dev.m_cdev, ap3216c_dev.devid, 1);///3.class_createap3216c_dev.m_class = class_create(THIS_MODULE, AP3216C_NAME);if (IS_ERR(ap3216c_dev.m_class)) {return PTR_ERR(ap3216c_dev.m_class);}///4.device_create    ap3216c_dev.m_dev = device_create(ap3216c_dev.m_class, NULL, ap3216c_dev.devid, NULL, AP3216C_NAME);if (IS_ERR(ap3216c_dev.m_dev)) {return PTR_ERR(ap3216c_dev.m_dev);}/// for debug:if (device_create_file(ap3216c_dev.m_dev, &dev_attr_ap3216c_test) != 0) {printk("ap3216c device create failed\n");return -1;}ap3216c_dev.client = client;printk("ap3216c driver probe done\n");return 0;
}int ap3216c_remove(struct i2c_client *client)
{printk("ap3216c driver remove begin\n");///cdev_delcdev_del(&ap3216c_dev.m_cdev);///注销设备号unregister_chrdev(ap3216c_dev.devid, AP3216C_NAME);///device_destroydevice_destroy(ap3216c_dev.m_class, ap3216c_dev.devid);///class_desroyclass_destroy(ap3216c_dev.m_class);printk("ap3216c driver remove done\n");return 0;
}/// 传统方式匹配
static const struct i2c_device_id ap3216c_id[] = {{"alientek,ap3216c", 0}, {}
};/// 设备树方式匹配
static const struct of_device_id ap3216c_of_match[] = {{.compatible = "alientek,ap3216c"}
};static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.owner = THIS_MODULE,.name = "ap3216c",.of_match_table = ap3216c_of_match,},.id_table = ap3216c_id,
};static int __init ap3216c_init(void)
{int ret = 0;printk("ap3216c driver init begin\n");ret = i2c_add_driver(&ap3216c_driver);printk("ap3216c driver init done, ret:%d\n", ret);return ret;
}static int __exit ap3216c_exit(void)
{printk("ap3216c driver exit begin\n"); i2c_del_driver(&ap3216c_driver);printk("ap3216c driver exit done\n"); return 0;
}module_init(ap3216c_init);
module_exit(ap3216c_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xuzhangxin");

3.测试效果

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FENDI CLUB精酿啤酒与小麦的不解之缘

FENDI CLUB精酿啤酒与小麦之间这种联系体现在啤酒的酿造原料、口感特色以及文化内涵等多个方面。以下是关于这两者之间关系的详细分析&#xff1a; 一、酿造原料的紧密联系 小麦作为关键原料&#xff1a;FENDI CLUB精酿啤酒在酿造过程中&#xff0c;小麦是不可或缺的原料之一…

Mybatis 系列全解(1)——全网免费最细最全,手把手教,学完就可做项目!

Mybatis 系列全解&#xff08;1&#xff09; 1. 第一个小程序2. CURD 增删改查3. 模糊查询4. 配置解析4.1 核心配置文件4.2 环境配置4.3 属性4.4 类型别名4.5 设置4.6 映射器 mappers 1. 第一个小程序 1&#xff09;创建一个数据库&#xff0c;一个表&#xff0c;填入一些数据…

Python3简单实现与Java的Hutool库SM2的加解密互通

1、背景&#xff1a; 因业务需求&#xff0c;需要与某平台接口对接。平台是Java基于Hutool库实现的SM2加密解密&#xff0c;研究了下SM2的加解密算法&#xff0c;网上找的资料&#xff0c;都是说SM2【椭圆曲线】 公钥长【x,y分量 64字节】&#xff0c;私钥短【32字节】&#x…

华为---OSPF被动接口配置(四)

9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口&#xff0c;成为被动接口后&#xff0c;将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息&#xff0c;即已运行在OSPF协议…

FuTalk设计周刊-Vol.031

&#x1f525;AI漫谈 热点捕手 1、如何用自然语言 5 分钟构建个人知识库应用&#xff1f;我的 GPTs builder 尝试 开发者的想象力闸门一旦打开&#xff0c;迎接我们的必然是目不暇接的 AI 应用浪潮冲击。 链接https://sspai.com/post/84325 2、GPT-4 Turbo、功能融合&#x…

【机器学习】大模型驱动下的医疗诊断应用

摘要&#xff1a; 随着科技的不断发展&#xff0c;机器学习在医疗领域的应用日益广泛。特别是在大模型的驱动下&#xff0c;机器学习为医疗诊断带来了革命性的变化。本文详细探讨了机器学习在医疗诊断中的应用&#xff0c;包括疾病预测、图像识别、基因分析等方面&#xff0c;并…

LCR 142.训练计划IV

1.题目要求: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ int compare(const void* a,const void* b) {return (*(int*)a - *(int*)b); } struct ListNode* trainningPlan(struct ListNode* l1, struct Li…

【数据结构】第十九弹---C语言实现冒泡排序算法

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、冒泡排序基本思想 2、代码的初步实现 3、代码的优化 4、代码的测试 5、时空复杂度分析 6、模拟实现qsort 6.1、冒泡排序函数 6.2、交换数…

针对 AI 优化数据湖仓一体:使用 MinIO 仔细了解 RisingWave

RisingWave 是现代数据湖仓一体处理层中的开源流数据库&#xff0c;专为性能和可扩展性而构建。RisingWave 旨在允许开发人员在流数据上运行 SQL。鉴于 SQL 是数据工程的通用语言&#xff0c;此功能非常重要。它具有强大的架构&#xff0c;包括计算节点、元节点和压缩器节点&am…

分享一个 Fail2ban 过滤规则

今天明月给大家分享个 Fail2ban 的过滤&#xff08;Filter&#xff09;规则&#xff0c;有关 Fail2ban 的文章大家可以参考【服务器全面使用 Fail2Ban 初见成效】和【使用 Fail2ban 禁止垃圾采集爬虫&#xff0c;保护 Nginx 服务器】等文了解&#xff0c;总之 Fail2ban 是 Linu…

分流井设备的监测控制和智慧运维

分流井是一种用于将雨水和污水进行分离的设施&#xff0c;通常设置在雨水管和污水管的汇合处。它可以根据不同的情况&#xff0c;将雨水和污水分别排放到不同的管道中&#xff0c;从而实现雨污分流的目的。 以下是一些常见的分流井类型和工作原理&#xff1a; 1、智能分流井&a…

java-SpringBoot执行定时任务-任务调度-@EnableScheduling和@Scheduled

文章目录 java借助springBoot框架&#xff0c;执行定时任务0. 项目地址1. 需求分析2、新建springBoot项目3. 编写定时任务3.1 开启调度任务3.2 编写定时任务方法 java借助springBoot框架&#xff0c;执行定时任务 0. 项目地址 https://github.com/OrangeHza/JavaDemo 1. 需求…

Redis小对象压缩

小对象压缩存储 如果Redis内部管理的集合数据结构很小&#xff0c;他会使用紧凑存储形式压缩存储。 Redis的ziplist是一个紧凑的字节数组结构&#xff0c;如下图所示&#xff0c;每个元素之间都是紧挨着的。 如果他存储的是hash结构&#xff0c;那么key和value会作为两个ent…

竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

Maven添加reactor依赖失败

目录 情况说明 解决过程 情况说明 起初是自己在学spring boot3&#xff0c;结果到了reactor这一部分的时候&#xff0c;在项目的pom.xml文件中添加下列依赖报错&#xff1a; <dependencyManagement><dependencies><dependency><groupId>io.projectr…

【CPP】插入排序、希尔排序

目录 1.插入排序1.1直接插入排序简介代码分析 1.2直接插入对比冒泡排序简介代码对比分析(直接插入排序与冒泡的复杂度效率区别) 1.3希尔排序简介代码分析 1.插入排序 基本思想&#xff1a;把一个待排数字按照关键码值插入到一个有序序列中&#xff0c;得到一个新的有序序列。 …

前沿技术丨S2S自动化测试解决方案

技术背景 随着面向服务的架构&#xff08;Service-Oriented Architecture&#xff0c;SOA&#xff09;在整车架构中的逐步推进及应用&#xff0c;车内网络通信中会一直并存基于以太网的面向服务和基于传统网络的面向信号的两类控制器&#xff0c;S2S&#xff08;Signal to Ser…

AXI学习笔记

文章目录 AXI口诀&#xff1a;AXI三种总线&#xff0c;三种接口&#xff0c;一个协议背景知识一、 AMBA&#xff1a;二、AXI2.1 通信协议与握手机制2.2 AXI协议特点2.3 三种AXI总线类型&#xff08;AXI4、AXI4-lite、AXI4-stream&#xff09;2.3.1 AXI通道&#xff08;5通道&am…

GD32 MCU的选项字节是什么?

GD32 MCU的选项字节是什么&#xff0c;有什么功能呢&#xff1f;选项字节被误篡改如何回复&#xff1f; 读者朋友们是否会有以上的疑问&#xff0c;首先我们先为大家介绍选项字节是什么以及选项字节的功能。 以GD32F30X系列MCU为例&#xff0c;其选项字节说明如下表所示&…

力扣每日一题 6/22 字符串/贪心

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 2663.字典序最小的美丽字符串【困难】 题目&#xff1a; 如果一个字符串满…