Use of device_attr
With Device_attr, you can add a "file" in SYS fs and modify the contents of the file to achieve the purpose of dynamically controlling the device during operation.
Similarly, there are driver_attr,bus_attr,class_attr.
The difference between these things is that the device_attr corresponding files in the/sys/devices/directory corresponding to the DEVICE below.
and several others are in the corresponding directory in Driver,bus,class.
This time the main introduction device_attr, the other several similar.
In Documentation/driver-model/device.txt, there is a detailed introduction to Device_attr, here is the main description of how to use.
First look at the prototype of Device_attr:
Device_attr (_name, _mode, _show, _store)
_name: Name, which is the name of the file that will be generated in SYS FS.
_mode: The above file access rights, the same as the normal file, Ugo format.
_show: This function is called when the file is displayed as a function of cat.
_store: Write function, echo content to the file, this function is called.
See how we populate these elements:
Name can be a casual, easy to remember, and can reflect its function can be.
The pattern can be read-only 0444, write only 0222, or read or write 0666 of the rows. Of course, the user\group\other can also be distinguished.
The display and write functions need to be implemented.
Displays the general implementation of the function:
Static ssize_t xxx_show (struct device *dev,
struct Device_attribute *attr, char *buf)
{
Return scnprintf (buf, Page_size, "%d\n", Dma_dump_flag);
}
The implementation is relatively simple, call a very Yangchun scnprintf, put the data into the BUF, even if it is done.
As to how the contents of BUF are displayed, this is skipped first.
General implementations of write functions:
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;
}
Also quite straightforward, do not elaborate.
It adds a lock for mutual exclusion.
The suffix _show and _store in the function name are certainly not required.
Just easy to identify.
Examples of DEVICE_ATTR definitions:
static device_attr (XXX, 0666, xxx_show, Xxx_store);
The code can prevent any location of the file, as long as it does not cause compilation errors!
Is that the way it's done?
Of course not, you also need to call the function Device_create_file to preach the files in SYS FS.
Call Method:
Device_create_file (&pdev->dev, &dev_attr_xxx);
The file is not created in a device directory, Pdev->dev is the device.
Dev_attr_xxx is to add dev_attr_ before xxx, as if it is nonsense, but the reality is this.
Start looking for a long while, where dev_attr_xxx definition?
Finally found here is the only place where it appears.
Example of Device_create_file invocation:
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;
}
This code is best placed in the probe function of the device.
Why, it is explained in Documentation/driver-model/device.txt.
Here's a look at the definition of device_attr:
#define DEVICE_ATTR (_name, _mode, _show, _store) \
struct Device_attribute dev_attr_# #_name = __attr (_name, _mode, _show, _store)
dev_attr_# #_name!!!!!
Finally found the dev_attr_xxx definition of the place!
#define __ATTR (_name,_mode,_show,_store) {\
. attr = {. Name = __stringify (_name),. Mode = _mode}, \
. Show = _show, \
. store = _store, \
}
Device_attribute definition:
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);
};
The function of device_attr is to define a Device_attribute structure object.
Device_create_file uses this object to create files under 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);
}
The type of
SD is struct sysfs_dirent.
/*
* sysfs_dirent-the building block of Sysfs hierarchy. each and
* every SYSFS node is repr Esented by single sysfs_dirent.
*
* as long as S_count reference is held, the sysfs_dirent itself is
* accessible. Deref Erencing 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;
Allocates space and initializes part of the member.
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 was called when the caller was 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 the parent inode if @sd is a directory and link into the
* Children list of the parent.
*
* This function should is 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 () is released and removed Sysfs_dirents are
* Cleaned up.
*
* LOCKING:
* Sysfs_mutex is released.
*/
Sysfs_addrm_finish (&ACXT);
if (RC)
Sysfs_put (SD);
return RC;
}
Use of device_attr