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 reset
    mdelay(20); ///< 最少等待10ms
    value = 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 reset
    
    printk("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_add
    cdev_init(&ap3216c_dev.m_cdev, &ap3216c_fops);
    cdev_add(&ap3216c_dev.m_cdev, ap3216c_dev.devid, 1);

    ///3.class_create
    ap3216c_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_del
    cdev_del(&ap3216c_dev.m_cdev);

    ///注销设备号
    unregister_chrdev(ap3216c_dev.devid, AP3216C_NAME);

    ///device_destroy
    device_destroy(ap3216c_dev.m_class, ap3216c_dev.devid);
    
    ///class_desroy
    class_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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度神经网络——什么是小样本学习?

引言 小样本学习是指使用极少量的训练数据来开发人工智能模型的各种算法和技术。小样本学习致力于让人工智能模型在接触相对较少的训练实例后识别和分类新数据。小样本训练与训练机器学习模型的传统方法形成鲜明对比&#xff0c;传统方法通常使用大量训练数据。小样本学习是 主…

DAY10-力扣刷题

1.最后一个单词的长度(简单) 58. 最后一个单词的长度 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子…

机器学习python实践——由特征选择引发的关于卡方检验的一些个人思考

最近在用python进行机器学习实践&#xff0c;在做到特征选择这一部分时&#xff0c;对于SelectPercentile和SelectKBest方法有些不理解&#xff0c;所以去了查看了帮助文档&#xff0c;但是在帮助文档的例子中出现了"chi2"&#xff0c;没接触过&#xff0c;看过去就更…

MySQL存储管理(一):删数据

从表中删除数据 从表中删除数据&#xff0c;也即是delete过程。 什么是表空间 表空间可以看做是InnoDB存储引擎逻辑结构的最高层&#xff0c;所有的数据都存放在表空间中。默认情况下&#xff0c;InnoDB存储引擎有一个共享表空间idbdata1&#xff0c;即所有数据都存放在这个表…

20.Cargo和Crates.io

标题 一、采用发布配置自定义构建1.1 默认配置1.2 修改配置项 二、将crate发布到Crates.io2.1 编写文档注释2.2 常用&#xff08;文档注释&#xff09;部分2.3 文档注释作用测试2.4 为包含注释的项添加文档注释2.5 使用pub use导出公有API2.6 创建Crates.io账号2.7 发布2.8 版本…

基于STM8系列单片机驱动LCD12864液晶程序

1&#xff09;单片机/ARM硬件设计小知识&#xff0c;分享给将要学习或者正在学习单片机/ARM开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 LCD12864支持串行和并行的通信传输方式&#xff…

NeRF从入门到放弃3: EmerNeRF

https://github.com/NVlabs/EmerNeRF 该方法是Nvidia提出的&#xff0c;其亮点是不需要额外的2D、3Dbox先验&#xff0c;可以自动解耦动静field。 核心思想&#xff1a; 1. 动、静filed都用hash grid编码&#xff0c;动态filed比静态多了时间t&#xff0c;静态的hash编码输入是…

C语言笔试题:实现把一个无符号整型数字的二进制序列反序后输出

目录 题目 实例 方法一&#xff1a;直接交换 方法二&#xff1a;间接交换 拓展 题目 编写一个函数&#xff0c;将一个无符号整数的所有位逆序&#xff08;在32位机器下&#xff09; 实例 例如有一个无符号整数 unsigned int num 32; unsigned int 在32位系统中占4个字…

如何选择优质智慧公厕系统厂家?@光明源

随着智慧城市建设的推进&#xff0c;智慧公厕系统成为提升城市公共服务水平的重要一环。选择一家优质的智慧公厕系统厂家不仅能确保设备的先进性和可靠性&#xff0c;还能提升用户体验和管理效率。以下是选择优质智慧公厕系统厂家的关键要素。 1. 厂家资质和信誉 1.1 资质认证…

VS C++常用错误与解决方法

无法找到 v143 的生成工具(平台工具集 “v143”) 若要使用 v143 生成工具进行生成&#xff0c;请安装 v143 生成工具。或者&#xff0c;可以升级到当前 Visual Studio 工具&#xff0c;方式是通过选择“项目”菜单或右键单击该解决方案&#xff0c;然后选择“重定解决方案目标…

外星人Alienware m18R1 原厂Windows11系统

装后恢复到您开箱的体验界面&#xff0c;包括所有原机所有驱动AWCC、Mydell、office、mcafee等所有预装软件。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http://w…

绝对值不等式——AcWing 104. 货仓选址

绝对值不等式 定义 与数学中的绝对值不等式定义一致&#xff0c;即含有绝对值符号的不等式。 运用情况 在一些需要根据数值与特定值的距离关系来进行判断和处理的算法中。用于对数据范围进行约束和界定。 注意事项 确保对绝对值的处理正确&#xff0c;尤其是在复杂的逻辑…

基于chatgpt-on-wechat搭建个人知识库微信群聊机器人

前言 啊&#xff0c;最近在别人微信群里看到一个聊天机器人&#xff0c;感觉挺好玩的。之前GPT刚出来的时候就知道有人把聊天机器人接入到微信或者QQ中来增加互动&#xff0c;但是当时没想那个想法。 很久没关注这块了&#xff0c;发现现在可以使用大模型知识库的方式来打造自…

【面试干货】Hashtable 与 HashMap 的区别

【面试干货】Hashtable 与 HashMap 的区别 1、线程安全性2、对null值的处理3、遍历方式4、遍历示例5、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;Hashtable和HashMap都是基于哈希表实现的Map接口。然而&#…

[Django学习]前端+后端两种方式处理图片流数据

方式1&#xff1a;数据库存放图片地址,图片存放在Django项目文件中 1.首先&#xff0c;我们现在models.py文件中定义模型来存放该图片数据,前端传来的数据都会存放在Django项目文件里的images文件夹下 from django.db import modelsclass Image(models.Model):title models.C…

Windows10任务栏卡顿解决方案

一、重新启动任务资源管理器 右键底部任务栏选择“任务管理器”&#xff1b;按快捷键“CtrlShiftEsc”&#xff1b;搜索框搜索“任务管理器”并单击“打开”&#xff1b;“WinX”打开开始菜单附属菜单&#xff0c;在列表中选择“任务管理器” &#xff1b;按下“ctrlaltdelete”…

24年安克创新社招入职自适应能力cata测评真题分享北森测评高频题库

第一部分&#xff1a;安克创新自适应能力cata测评 感谢您关注安克创新社会招聘&#xff0c;期待与您一起弘扬中国智造之美。 为对您做出全面的评估&#xff0c;现诚邀您参加我们的在线测评。 测评名称&#xff1a;社招-安克创新自适应能力cata测评 第二部分&#xff1a;安克…

容器之笔记本构件演示

代码&#xff1a; #include <gtk-2.0/gtk/gtk.h> #include <glib-2.0/glib.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <stdio.h>void rotate_book(GtkButton *button, GtkNotebook *notebook) {gtk_notebook_set_tab_pos(notebook, (notebook…

Linux驱动开发(三)--新字符设备驱动开发 LED驱动开发升级

1、新字符设备驱动原理 使用 register_chrdev 函数注册字符设备的时候只需要给定一个主设备号即可&#xff0c;但是这样会 带来两个问题 需要我们事先确定好哪些主设备号没有使用 会将一个主设备号下的所有次设备号都使用掉&#xff0c;比如现在设置 LED 这个主设备号为200&…

这周,接连两位程序员猝死...

这周接连发生了两起不幸的事。俩位程序员去世的消息&#xff0c;深感悲伤和惋惜。 6月17号下午&#xff0c;一位负责研发的女员工在虾皮研发中心办公室猝死&#xff0c;年仅 30 岁。 官方通告&#xff1a; 同一天&#xff0c;另一位科大讯飞的高级测试工程师在家突发不适离世…