DEVICE_ATTR的使用
使用DEVICE_ATTR
,可以在sysfs中添加“文件”,通过修改该文件内容,可以实现在运行过程中动态控制device的目的。
类似的还有DRIVER_ATTR
,BUS_ATTR
,CLASS_ATTR
。
他们的区别就是,DEVICE_ATTR
对应的文件在/sys/devices/目录中对应的device下面,而其他几个分别在driver,bus,class中对应的目录下。
这次主要介绍DEVICE_ATTR
,其他几个类似。
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
函数宏DEVICE_ATTR
内封装的是__ATTR(_name,_mode,_show,_stroe)
方法,_show
表示的是读方法,_stroe
表示的是写方法。
当然_ATTR
不是独生子女,他还有一系列的姊妹__ATTR_RO
宏只有读方法,__ATTR_NUL
等等
对设备的使用 DEVICE_ATTR
,对总线使用BUS_ATTR
,对驱动使用DRIVER_ATTR
,对类 别 (class) 使用 CLASS_ATTR
, 这四个高级的宏来自于
static ssize_t xxx_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n", dma_dump_flag);
}
实现相对简单,调用了个很阳春的scnprintf,把数据放到buf中,就算大功告成了。
至于buf中的内容怎么显示出来,这个先略过。
写入函数的一般实现:
static ssize_t xxx_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long num;
if (strict_strtoul(buf, 0, &num))
return -EINVAL;
if (num < 0)
return -EINVAL;
mutex_lock(&xxx_lock);
dma_dump_flag = num;
mutex_unlock(&xxx_lock);
return count;
}
也挺直白,就不细说了。
其中加了个lock进行互斥。
函数名中的后缀_show
和_store
当然不是必须的。
只是便于标识。
DEVICE_ATTR
的定义例子:
static DEVICE_ATTR(xxx, 0666, xxx_show, xxx_store);
该代码可以防止文件的任何位置,只要别引起编译错误!
是不是这样就搞定了?
当然没有,还需要调用函数device_create_file
来传教sys fs中的文件。
调用方法:
device_create_file(&pdev->dev, &dev_attr_xxx);
文件不是创建在某个device目录下么,pdev->dev
就是该device。
dev_attr_xxx
就是在xxx前加上dev_attr
_,好像是废话,不过现实就是这样。
开始还找了半天,dev_attr_xxx
在哪儿定义?
最终发现这儿就是它唯一出现的地方。
device_create_file
的调用例子:
ret = device_create_file(&pdev->dev, &dev_attr_xxx);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to create xxx sysfs files: %d\n", ret);
return ret;
}
这个代码最好放在device的probe函数中。
原因么,在documentation/driver-model/Device.txt中有说明。
下面看看DEVICE_ATTR
的定义:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
dev_attr_##_name!
终于找到dev_attr_xxx
定义的地方了!
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
device_attribute
定义:
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
的功能就是定义一个device_attribute
结构体对象。
device_create_file
利用该对象在device下创建文件。
/**
* device_create_file - create sysfs attribute file for device.
* @dev: device.
* @attr: device attribute descriptor.
*/
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;
}
/**
* sysfs_create_file - create an attribute file for an object.
* @kobj: object we're creating for.
* @attr: attribute descriptor.
*/
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
sd的类型为struct sysfs_dirent。
/*
* sysfs_dirent - the building block of sysfs hierarchy. Each and
* every sysfs node is represented by single sysfs_dirent.
*
* As long as s_count reference is held, the sysfs_dirent itself is
* accessible. Dereferencing s_elem or any other outer entity
* requires s_active reference.
*/
struct sysfs_dirent {
atomic_t s_count;
atomic_t s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
struct sysfs_dirent *s_parent;
struct sysfs_dirent *s_sibling;
const char *s_name;
const void *s_ns; /* namespace tag */
union {
struct sysfs_elem_dir s_dir;
struct sysfs_elem_symlink s_symlink;
struct sysfs_elem_attr s_attr;
struct sysfs_elem_bin_attr s_bin_attr;
};
unsigned int s_flags;
unsigned short s_mode;
ino_t s_ino;
struct sysfs_inode_attrs *s_iattr;
};
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
int type)
{
return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
}
int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
const struct attribute *attr, int type, mode_t amode)
{
umode_t mode = (amode & S_IALLUGO) | S_IFREG;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
// 分配空间,并初始化部分成员。
sd = sysfs_new_dirent(attr->name, mode, type);
if (!sd)
return -ENOMEM;
sd->s_attr.attr = (void *)attr;
/*
* Initialize a lock instance's lock-class mapping info:
*/
sysfs_dirent_init_lockdep(sd);
/**
* sysfs_addrm_start - prepare for sysfs_dirent add/remove
* @acxt: pointer to sysfs_addrm_cxt to be used
* @parent_sd: parent sysfs_dirent
*
* This function is called when the caller is about to add or
* remove sysfs_dirent under @parent_sd. This function acquires
* sysfs_mutex. @acxt is used to keep and pass context to
* other addrm functions.
*
* LOCKING:
* Kernel thread context (may sleep). sysfs_mutex is locked on
* return.
*/
sysfs_addrm_start(&acxt, dir_sd);
/**
* sysfs_add_one - add sysfs_dirent to parent
* @acxt: addrm context to use
* @sd: sysfs_dirent to be added
*
* Get @acxt->parent_sd and set sd->s_parent to it and increment
* nlink of parent inode if @sd is a directory and link into the
* children list of the parent.
*
* This function should be called between calls to
* sysfs_addrm_start() and sysfs_addrm_finish() and should be
* passed the same @acxt as passed to sysfs_addrm_start().
*
* LOCKING:
* Determined by sysfs_addrm_start().
*
* RETURNS:
* 0 on success, -EEXIST if entry with the given name already
* exists.
*/
rc = sysfs_add_one(&acxt, sd);
/**
* sysfs_addrm_finish - finish up sysfs_dirent add/remove
* @acxt: addrm context to finish up
*
* Finish up sysfs_dirent add/remove. Resources acquired by
* sysfs_addrm_start() are released and removed sysfs_dirents are
* cleaned up.
*
* LOCKING:
* sysfs_mutex is released.
*/
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}