当前位置: 首页 > 工具软件 > Device > 使用案例 >

linux使用DEVICE_ATTR创建设备节点(常用)

胡翔
2023-12-01

DEVICE_ATTR是一个宏,其定义在include/linux/device.h文件:

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

__ATTR宏定义在include/linux/sysfs.h:

#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode },   \
.show   = _show,                                        \
.store  = _store,                                       \
}

struct device_attribute结构定义在include/linux/device.h文件:

/* interface for exporting device attributes */
struct device_attribute {
struct attribute    attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
        char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
         const char *buf, size_t count);
};

DEVICE_ATTR宏展开:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = { \
    .attr = {.name = __stringify(_name), .mode = _mode },   \
    .show   = _show,                                        \
    .store  = _store,                                       \
}

举例说明DEVICE_ATTR使用方法:

static ssize_t gpio_test_show(struct device *dev,
               struct device_attribute *attr, char *buf)
{
int temp[5];
struct gpio_data *gpio_data_ptr = dev_get_drvdata(dev);

temp[0] = gpio_get_value(gpio_data_ptr->gpio0);
temp[1] = gpio_get_value(gpio_data_ptr->gpio2);
temp[2] = gpio_get_value(gpio_data_ptr->gpio3);
temp[3] = gpio_get_value(gpio_data_ptr->gpio10);
temp[4] = gpio_get_value(gpio_data_ptr->gpio11);

return snprintf(buf, 6, "%d%d%d%d%d", temp[0], temp[1],
                temp[2], temp[3], temp[4]);
}

static ssize_t gpio_test_store(struct device *dev,
       struct device_attribute *attr, const char *buf, size_t size)
{
struct gpio_data *gpio_data_ptr = dev_get_drvdata(dev);

gpio_set_value(gpio_data_ptr->gpio0,  (buf[0] == '1') ? 1 : 0);
gpio_set_value(gpio_data_ptr->gpio2,  (buf[1] == '1') ? 1 : 0);
gpio_set_value(gpio_data_ptr->gpio3,  (buf[2] == '1') ? 1 : 0);
gpio_set_value(gpio_data_ptr->gpio10, (buf[3] == '1') ? 1 : 0);
gpio_set_value(gpio_data_ptr->gpio11, (buf[4] == '1') ? 1 : 0);

return size;
}

然后调用DEVICE_ATTR这个宏:

static DEVICE_ATTR(gpio_test, 0664, gpio_test_show, gpio_test_store);

//注意:有些平台,例如rk3288 android7.1不能使用0666的权限,否则会报如下错,要改成0664或者0644:
include/linux/bug.h:33:45: error: negative width in bit-field '<anonymous>'

之后在驱动的probe函数中即可调用device_create_file:

err = device_create_file(&dev->dev, &dev_attr_gpio_test);
if (err) {
    dev_err(&dev->dev, "sys file creation failed\n");
    return -ENODEV;
}

device_create_file和device_remove_file定义在drivers/base/core.c中,声明在include/linux/device.h中,device_create_file返回0代表创建成功。

int device_create_file(struct device *dev, const struct device_attribute *attr)
{
int error = 0;

if (dev) {
    WARN(((attr->attr.mode & S_IWUGO) && !attr->store),
        "Attribute %s: write permission without 'store'\n",
        attr->attr.name);
    WARN(((attr->attr.mode & S_IRUGO) && !attr->show),
        "Attribute %s: read permission without 'show'\n",
        attr->attr.name);
    error = sysfs_create_file(&dev->kobj, &attr->attr);
}

return error;
}

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

在文件系统/sys/devices/soc.0/gpio_ctrl.72下面生成gpio_test节点,用cat,echo操作硬件:

root@msm8909:/sys/devices/soc.0/gpio_ctrl.72 # ls -l gpio_test                  
-rw-rw-r-- root     root         4096 1970-01-01 00:02 gpio_test
root@msm8909:/sys/devices/soc.0/gpio_ctrl.72 # cat gpio_test
00000
root@msm8909:/sys/devices/soc.0/gpio_ctrl.72 # echo 11111 > gpio_test

下面是一个简单的Demo程序,主要是利用DEVICE_ATTR创建设备节点:

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h>

static int gpio_dcin;


#if 0
static int test_power_work_func(void)
{
int value;
value = get_adc_sample(0, CHAN_1);
printk("personicore_power: current power adc value is %d\n", value);
return value;
}
#endif

static ssize_t personicore_power_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
int value;
return sprintf(buf, "%d\n", value = 0);//= test_power_work_func());
}


static ssize_t personicore_power_store(struct device *dev,
			struct device_attribute *attr,
			const char *buf, size_t size)
{
return size;
}

static ssize_t dcin_det_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", gpio_get_value(gpio_dcin));
}


static ssize_t dcin_det_store(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t size)
{
return size;
}

static DEVICE_ATTR(personicore_power, 0664, personicore_power_show, personicore_power_store);
static DEVICE_ATTR(dcin_det, 0664, dcin_det_show, dcin_det_store);

static int personicore_power_probe(struct platform_device *pdev)
{

struct device_node *personicore_power_node = pdev->dev.of_node;
enum of_gpio_flags flags;

printk("enter %s start..\n", __func__);

gpio_dcin = of_get_named_gpio_flags(personicore_power_node, "gpio_dcin", 0, &flags);
printk("gpio_dcin is %d --\n", gpio_dcin);
if (!gpio_is_valid(gpio_dcin)) {
	printk("gpio_dcin: %d is invalid\n", gpio_dcin);
	return -ENODEV;
}
if (gpio_request(gpio_dcin, "gpio_dcin")) {
	printk("gpio_dcin: %d request failed!\n", gpio_dcin);
	gpio_free(gpio_dcin);
	return -ENODEV;
}

gpio_direction_input(gpio_dcin);
printk("gpio_dcin pin level is %d\n", gpio_dcin);

device_create_file(&pdev->dev, &dev_attr_personicore_power);
device_create_file(&pdev->dev, &dev_attr_dcin_det);

printk("___%s___ sucess!\n", __func__);

return 0;
}

static int personicore_power_remove(struct platform_device *pdv)
{
printk("___%s___\n", __func__);
return 0;
}

static struct of_device_id personicore_power_match_table[] = {
{ .compatible = "rk3288, personicore_power",},
{},
};

static struct platform_driver personicore_power_driver = {

.driver = {
	.name = "personicore_power",
	.owner = THIS_MODULE,
	.of_match_table = personicore_power_match_table,
},
.probe = personicore_power_probe,
.remove = personicore_power_remove,
};

static int personicore_power_init(void)
{
printk("___%s___\n", __func__);
platform_driver_register(&personicore_power_driver);
return 0;
}

static void personicore_power_exit(void)
{
printk("___%s___\n", __func__);
platform_driver_unregister(&personicore_power_driver);

}

module_init(personicore_power_init);
module_exit(personicore_power_exit);

MODULE_AUTHOR("cai.zhongding");
MODULE_DESCRIPTION("Device_create Driver for personicore_power");
MODULE_LICENSE("GPL");

下面是dts节点部分:

personicore_power {

	compatible = "rk3288, personicore_power";
	status = "okay";
	gpio_dcin = <&gpio0 18 GPIO_ACTIVE_HIGH>; //gpio0_C2
};
 类似资料: