Linux hwmon子系统分析2

Linux hwmon子系统分析2(基于Linux6.6)---hwmon driver开发实践介绍

 

一、hwmon 子系统的框架

hwmon子系统的架构如下,主要借助sysfs文件系统及相关接口、设备驱动模型中class、device相关的接口,实现了针对hwmon芯片各主要参数的访问。如下图所示,针对hwmon子系统驱动而言,需要做的内容如下:

  1. 针对每一个hwmon芯片的参数,均实现struct sensor_device_attribute或sensor_device_attribute类型的变量(包括index、struct device_attribute类型变量(主要实现读写访问接口))

二、hwmon driver开发流程说明

针对hwmon driver的开发流程,其实现步骤大致如下:

  1. 为该hwmon的主要参数(如温度传感器的当前温度、最大温度、最小温度、最大报警温度、最小报警温度等),均实现struct sensor_device_attribute或sensor_device_attribute类型的变量,并提供针对该参数的读写方法(show、store);
  2. 调用devm_hwmon_device_register_with_groups,完成hwmon device的注册。

三、实现一个虚拟的温度传感器驱动

在 Linux hwmon 驱动开发过程中,主要的步骤是为硬件监控设备(如温度传感器、风扇转速、功率等)提供相应的参数、实现 showstore 函数来读写这些参数,并通过 devm_hwmon_device_register_with_groups() 函数注册设备及其属性。以下是详细的开发流程和实现步骤。

1. 定义硬件监控设备的参数

首先,需要定义硬件监控设备的相关参数,例如温度、最大温度、最小温度、最大报警温度、最小报警温度等。这些参数通常会使用 sensor_device_attribute 结构体进行定义,sensor_device_attribute 结构体包含了属性的名称、读取和写入方法。

#include <linux/hwmon.h>
#include <linux/sysfs.h>

struct sensor_device_attribute my_temp_attr[] = {
    SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp_show, NULL, 0),  // 读操作
    SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, temp_show, temp_store, 1),  // 读写操作
    SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, temp_alarm_show, NULL, 1),  // 只读报警
    SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, temp_show, temp_store, 2),  // 读写操作
    SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, temp_alarm_show, NULL, 2),  // 只读报警
};

2. 实现属性的 showstore 函数

每个 sensor_device_attribute 结构体都需要对应一个 show 和可选的 store 函数。show 函数用于读取硬件传感器的数据,而 store 函数则用于设置硬件的相关参数。

例如,读取温度的 show 函数和设置温度的 store 函数可以如下实现:

static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    int temp = read_temperature_from_sensor();  // 读取硬件传感器的温度值
    return snprintf(buf, PAGE_SIZE, "%d\n", temp);  // 返回温度值
}

static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
{
    int temp;
    if (sscanf(buf, "%d", &temp) == 1) {
        set_temperature_on_sensor(temp);  // 设置硬件传感器的温度值
        return count;
    }
    return -EINVAL;
}

在这个例子中,temp_show 用于读取温度传感器的数据,temp_store 用于设置温度阈值。

3. 注册硬件监控设备

当所有的属性和对应的操作函数都实现完毕后,需要将该设备注册到 hwmon 子系统,并将属性与设备进行绑定。可以通过 devm_hwmon_device_register_with_groups() 函数来完成设备的注册。

devm_hwmon_device_register_with_groups() 函数会将设备与相关的属性组(由 sensor_device_attribute 数组组成)进行绑定,并将设备注册到 hwmon 子系统中。

static int my_hwmon_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    int err;

    // 注册 hwmon 设备,并将属性组与设备关联
    err = devm_hwmon_device_register_with_groups(dev, "my_hwmon", NULL, my_temp_attr);
    if (err) {
        dev_err(dev, "Failed to register hwmon device\n");
        return err;
    }

    return 0;
}

4. 注册硬件监控设备的属性

当设备被注册时,内核会为该设备自动创建一个 /sys/class/hwmon/ 目录,并将所有的属性文件(例如 temp1_input, temp1_max, temp1_min 等)链接到该目录中。用户空间程序可以通过读取这些文件来获取硬件的监控数据,也可以通过写这些文件来设置硬件的某些参数。

5. 硬件监控设备卸载

当硬件设备不再需要时,应该卸载相应的驱动并清理资源。这个过程通常在 remove 函数中进行:

static int my_hwmon_remove(struct platform_device *pdev)
{
    // 无需显式注销属性,devm_hwmon_device_register_with_groups 会自动管理
    return 0;
}

6. 示例代码总结

以下是完整的示例代码,展示了如何为一个简单的温度传感器编写 hwmon 驱动。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/uaccess.h>

static struct class *hwmon_class;
static struct device *hwmon_device;
static int current_temperature = 25000; // 假设温度为 25.000°C(单位是毫度)

// sysfs 属性:用于读取温度
static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%d\n", current_temperature);
}

// sysfs 属性:用于设置温度阈值
static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    int temp;
    if (sscanf(buf, "%d", &temp) == 1) {
        current_temperature = temp;  // 设置新的温度
        return count;
    }
    return -EINVAL;
}

// 创建一个 sysfs 属性:temp1_input
static DEVICE_ATTR(temp1_input, S_IRUGO, temp_show, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, temp_show, temp_store);

static struct attribute *attrs[] = {
    &dev_attr_temp1_input.attr,
    &dev_attr_temp1_max.attr,
    NULL,
};

static struct attribute_group attr_group = {
    .attrs = attrs,
};

// 驱动的 probe 和 remove 函数
static int my_hwmon_probe(struct platform_device *pdev)
{
    int ret;

    // 创建 sysfs 接口
    hwmon_device = device_create(hwmon_class, &pdev->dev, 0, NULL, "hwmon_device");
    if (IS_ERR(hwmon_device)) {
        ret = PTR_ERR(hwmon_device);
        pr_err("Failed to create device: %ld\n", ret);
        return ret;
    }

    // 创建并注册 sysfs 属性组
    ret = sysfs_create_group(&hwmon_device->kobj, &attr_group);
    if (ret) {
        pr_err("Failed to create sysfs group\n");
        device_unregister(hwmon_device);
        return ret;
    }

    pr_info("HWMon driver probed successfully\n");
    return 0;
}

static int my_hwmon_remove(struct platform_device *pdev)
{
    // 删除 sysfs 属性组
    sysfs_remove_group(&hwmon_device->kobj, &attr_group);
    // 注销设备
    device_unregister(hwmon_device);
    pr_info("HWMon driver removed\n");
    return 0;
}

// 设备匹配表
static const struct of_device_id my_hwmon_of_match[] = {
    { .compatible = "myvendor,mytemperature", },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, my_hwmon_of_match);

// 平台驱动结构体
static struct platform_driver my_hwmon_driver = {
    .probe = my_hwmon_probe,
    .remove = my_hwmon_remove,
    .driver = {
        .name = "my_hwmon",
        .of_match_table = my_hwmon_of_match,
    },
};

module_platform_driver(my_hwmon_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("A simple temperature sensor driver using sysfs");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值