Linux驱动开发之DEVICE_ATTR调试技术

一、功能介绍

1、简介

在sysfs中添加device属性文件,可以动态控制device或者获取device信息。

2、相关函数、宏原型及功能

2.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结构的dev_attr_name变量。

参数:

_name:名称,在sysfs中device属性文件名称

_mode:访问权限,与普通文件相同,UGO格式

_show:显示函数,cat文件时调用此函数

_store:写函数,echo内容到文件时调用此函数

2.2、device_create_file

函数定义在kernel/drivers/base/core.c中。原型如下:

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:设备指针,需要描述属性的设备指针;

attr:device_attribute指针,即DEVICE_ATTR宏生成的dev_attr_name指针。

2.3、device_remove_file

函数定义在kernel/drivers/base/core.c中。原型如下:

void device_remove_file(struct device *dev, const struct device_attribute *attr)
{
    if (dev)

        sysfs_remove_file(&dev->kobj, &attr->attr);
}

功能:

移除device属性文件。

二、使用流程

1、创建device_attribute结构变量

DEVICE_ATTR(xxx,0755,xxx_show,xxx_store);

2、实现show、store函数

show。

static size_t xxx_show(struct device *dev,struct device_attribute *attr,char *buf)
{
    return sprintf(buf,"xxx\n");
}

store。

static size_t xxx_store(struct device *dev,struct device_attribute *attr,char *buf,size_t count)
{
    return count;
}

3、init函数中创建device属性文件

device_create_file(device, &dev_attr_xxx); 

4、exit函数中移除device属性文件

device_remove_file(device, &dev_attr_xxx); 

三、使用实例

1、源码

#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 <mach/gpio_drv.h>
#include <linux/gpio.h>

#include <asm/uaccess.h>
#include <asm/io.h>

#define chrtest_CNT			1		  	    /* 设备号个数 */
#define chrtest_NAME		"gpio_test"    	/* 名字 */

/* chrtest设备结构体 */
struct chrtest_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *dev;		/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
};

static struct chrtest_dev chrtest;

static struct file_operations chrtest_fops = {
	.owner = THIS_MODULE,
};

static ssize_t gpio_power_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	printk("---gpio_power:%d---\n", gpio_get_value(GPIO_POWER_EN));
	
	return 0;
}

static ssize_t gpio_power_store(struct device *dev, struct device_attribute *attr, char *buf, size_t len)
{
	int ret, regValue;
	
	ret = kstrtoint(buf, 0, &regValue);
	printk("value:%d\n", regValue);
	
	GPIO_OUTPUT(GPIO_POWER_EN, regValue);
	
	return len;
}
static DEVICE_ATTR(gpio_power, 0755, gpio_power_show, gpio_power_store);

static int __init led_init(void)
{
	u32 val = 0;
	int ret;

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (chrtest.major) {		/*  定义了设备号 */
		chrtest.devid = MKDEV(chrtest.major, 0);
		register_chrdev_region(chrtest.devid, chrtest_CNT, chrtest_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&chrtest.devid, 0, chrtest_CNT, chrtest_NAME);	/* 申请设备号 */
		chrtest.major = MAJOR(chrtest.devid);	/* 获取分配号的主设备号 */
		chrtest.minor = MINOR(chrtest.devid);	/* 获取分配号的次设备号 */
	}
	printk("new char device major=%d,minor=%d\n",chrtest.major, chrtest.minor);	
	
	/* 2、初始化cdev */
	chrtest.cdev.owner = THIS_MODULE;
	cdev_init(&chrtest.cdev, &chrtest_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&chrtest.cdev, chrtest.devid, chrtest_CNT);

	/* 4、创建类 */
	chrtest.class = class_create(THIS_MODULE, chrtest_NAME);
	if (IS_ERR(chrtest.class)) {
		return PTR_ERR(chrtest.class);
	}

	/* 5、创建设备 */
	chrtest.dev = device_create(chrtest.class, NULL, chrtest.devid, NULL, chrtest_NAME);
	if (IS_ERR(chrtest.dev)) {
		return PTR_ERR(chrtest.dev);
	}
	
	/*6、创建调试节点*/
	ret = device_create_file(chrtest.dev, &dev_attr_gpio_power);
	if (ret){
		printk("%s gpio_power device_create_file err!\n", __func__);
	} else {
		printk("%s gpio_power device_create_file success!\n", __func__);
	}
	
	return 0;
}

static void __exit led_exit(void)
{
	/* 注销字符设备驱动 */
	cdev_del(&chrtest.cdev);/* 删除cdev */
	unregister_chrdev_region(chrtest.devid, chrtest_CNT); /* 注销设备号 */

	device_destroy(chrtest.class, chrtest.devid);
	class_destroy(chrtest.class);

    /* 移除device属性节点 */
    device_remove_file(chrtest.dev,  &dev_attr_gpio_power);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

实例代码中,主要是针对了一个GPIO进行了读写,也就是获取它的电平高低和设置它的输出电平高低。

然后就是这个device属性节点是基于字符设备创建的,它生成的device节点路径我没记错的话,应该是在/sys/devices/virtual/gpio_test/gpio_test/。

2、节点使用

读:cat /sys/devices/virtual/chartest/gpio_test/gpio_test。

写:echo 1 > /sys/devices/virtual/chartest/gpio_test/gpio_test或者echo 0 > /sys/devices/virtual/chartest/gpio_test/gpio_test。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值