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

news2024/11/24 7:43:38

在平常做一些驱动调试的时候,每次都写应用去调试相对较麻烦,有一个非常便捷的操作方法就是使用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

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

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

相关文章

RK3568平台(音频篇)RT5651解码芯片Codec驱动分析

一.Audio Codec的必要性 在理想状况下&#xff0c;对于录音过程&#xff0c;只需要将麦克风获取到的analog信号通过ADC转换为digital信号并存储即可&#xff0c;对于播放音过程&#xff0c;只需要将digital信号通过DAC转换为analog并输出到speaker播放即可。 但在实际的过程中…

C++ 编程技巧分享

侯捷 C 学习路径&#xff1a;面向对象的高级编程 -> STL库 -> C11新特性 -> cmake 1.1. C 与 C的区别 在C语言中&#xff0c;主要存在两大类内容&#xff0c;数据和处理数据的函数&#xff0c;二者彼此分离&#xff0c;是多对多的关系。不同的函数可以调用同一个数据…

Docker开机自动重启及自动启动容器

Docker开机自动重启及自动启动容器 Windows开机自动重启设置容器自动启动 Windows开机自动重启 勾选 Start Docker Desktop when you sign in to your computer 设置容器自动启动 1.docker update 命令 Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]Update co…

32.基于分隔符解决黏包和半包

LineBasedFrameDecoder 基于换行/n (linux)或回车换行/r/n(windows)进行分割。 使用LIneBasedFrameDecoder构造方法,需要设定一个最大长度。 如果超过了最大长度,还是没有找到换行符,就这位这个数据段太长了,抛出ToolLongFrameException DelimiterBasedFrameDecoder …

IF膨胀时代,“水刊”当赢?2023热门“水刊”影响因子详解!

【欧亚科睿学术】 1 “四大水刊”详情 图片来源&#xff1a;欧亚科睿学术整理 “四大水刊”的影响因子均有所下跌&#xff0c;其中&#xff0c;曾经被列入中科院预警名单的期刊MEDICINE&#xff0c;其影响因子已是连续三年持续下降。从JCR分区来看&#xff0c;四本期刊分区均…

新手(初学者)学R语言第一课,从学正确导入数据开始

初看题目好像我在教你怎么导入数据&#xff0c;不不不&#xff0c;我是在教你正确的导入数据&#xff0c;不是说数据导入R就叫正确导入数据了。本章为新手教程&#xff0c;老手可以跳过。 这个内容早就想写了&#xff0c;今天有点空和大家聊一下。为什么R语言对于新手而言不太友…

threeJS 基础 03---动画

1.动画效果渲染循环 注&#xff1a; 使用循环渲染时&#xff0c;不用手动渲染到画布且再次调用监听更新事件&#xff0c;两者只用使用其中之一即可 周期循环&#xff0c;默认理想状态下每秒循环60次 requestAnimationFrame 渲染帧率&#xff08;详情见threeJs的文档&#xff…

区块链会议投稿资讯CCF A--WINE 2024 截止7.15 附录用率 附录用的区块链文章

Conference&#xff1a;The Conference on Web and Internet Economics (WINE) CCF level&#xff1a;CCF A Categories&#xff1a;Cross-cutting/comprehensive/emerging Year&#xff1a;2024 Conference time&#xff1a; December 2-5, 2024 录用率&#xff1a; sele…

Pyqt5 + Qt Creator实现QML开发环境配置

先安装Qt Creator, 该软件主要是为了编辑QML文件 在pycharm中配置外部插件&#xff0c;实现Qt Creator的调用 配置完成后&#xff0c;右击qml文件选择Qt Creator就可以直接进行编辑了

高效22KW双向DCDC储能、充电电源模块项目设计开发

22kW 双向CLL谐振变换器的目标是输出电压范围宽、高效率和高功率密度的双向应用&#xff0c;如电动汽车车载充电器和储能系统。研究了一种新的灵活的 CLLC 双向谐振变换器增益控制方案&#xff0c;以便在充放电模式下实现高效率和宽电压增益范围。得益于 Wolfspeed C3MTM 1200V…

Python+Selenium自动化入门

本章内容需有一定Python基础&#xff0c;如何不懂的&#xff0c;请先学习Python。 什么&#xff1f;&#xff1f;没有好的学习资料&#xff0c;给你准备好了&#xff01;&#xff01; Web自动化环境搭建 1、软件准备 python64位安装包chrome64位浏览器&驱动浏览器驱动下…

基于SpringBoot+协同过滤算法的家政服务平台设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

Java 超详细实现导入导出 (包含时间转换问题和样式)

序言 工作中遇到了导入导出问题&#xff0c;并且出现了导入或导出Excel时间格式变为数字的问题。通过学习解决实现了这些功能&#xff0c;记录总结分享给大家。本文将详细介绍如何使用 Java 编程语言和 Apache POI 库来实现这些功能。我们将通过一个示例项目演示如何从数据库中…

STM32学习笔记(十)--I2C、IIC总线协议详解

概述&#xff1a;Inter Integrated Circuit&#xff0c;一组多从 多组多从 有应答 是一种同步&#xff08;具有时钟线需要同步时钟SCL&#xff09;、串行&#xff08;一位一位的往一个方向发送&#xff09;、半双工&#xff08;发送接收存在一种&#xff09;通信总线。 &…

代码随想录算法训练营第六十七天 | 字符串接龙、有向图的完全可达性、岛屿的周长

字符串接龙 文字讲解&#xff1a;110. 字符串接龙 | 代码随想录 解题思路 本题只需要求出最短路径的长度就可以了&#xff08;想到广搜&#xff09;&#xff0c;不用找出具体路径。 所以这道题要解决两个问题&#xff1a; 图中的线是如何连在一起的起点和终点的最短路径长…

Stable Diffusion AI绘画助力建筑设计艺术创新——城市建筑设计大模型分享

大家好&#xff0c;我是向阳 今天我将针对建筑设计方面的AI大模型进行简单介绍&#xff0c;我们将通过富有想象力的关键词或结合Stable Diffusion 的ControlNet 给原本只有黑白线条的线稿变成彩色的效果图&#xff0c;可能你只需要短短几分钟就可以让黑白线稿变成几种甚至十几种…

临时关闭Windows安全中心

在使用WindowsOS是&#xff0c;微软安全中心是我们必不可少的安全防护&#xff0c;但有时我们也会产生想要将其关闭的需求&#xff0c;下面将要介绍如何临时关闭Windows的安全中心 一、打开安全中心、选择“病毒与威胁防护”&#xff0c;点击“管理设置” 之后将其实时保护关闭…

材料科学SCI期刊,中科院3区,收稿范围广,易录用

一、期刊名称 International Journal of Material Forming 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;材料科学 影响因子&#xff1a;2.4 中科院分区&#xff1a;3区 三、期刊征稿范围 该杂志发表和传播材料成型领域的原创研究。该研究应构成对材料…

multiprocessing多进程计算及与rabbitmq消息通讯实践

1. 需求与设计 我所设计的计算服务旨在满足多个客户对复杂计算任务的需求。由于这些计算任务通常耗时较长且资源消耗较大&#xff0c;为了优化客户体验并减少等待时间&#xff0c;我采取了并行计算的策略来显著提升计算效率。 为实现这一目标&#xff0c;我计划利用Python的m…

展讯-源码编译

1.硬件要求 编译主机要求&#xff0c;注意尽量不要使用虚拟机 CPU&#xff1a;Intel(R) Core(TM) i7-4790 CPU 3.60 GHz 内存&#xff1a;8G 硬盘&#xff1a;500G SSD Ubuntu 版本&#xff1a;Ubuntu 64-bit 14.04.5 LTS 这是最低版本要求&#xff0c;实际性能越高越好 2…