Linux內核修煉之Low-Level Sysfs Operations理解

訪問設備有兩種途徑: 一種是創建/dev設備節點,另一種是通過sysfs,這兩種都可以通過我們熟悉的open,read,write來操作了(unix哲學:萬物皆文件)。

sysfs對用戶可見,也就是說我們可以在用戶空間通過sysfs所提供的接口來訪問設備(當然寫驅動時要完成sysfs相關接口--low-level sysfs operations)

其實sysfs不只是對設備節點來說,無論是bus,driver等,kobject,linux統一設備模型最基本的對象,記住這一點很重要,sysfs提供的low-level sysfs操作,需要好好理解了...

下文來自ldd3. http://www.makelinux.net/ldd3/chp-14-sect-2

14.2. Low-Level Sysfs Operations

Kobjects are the mechanism behind the sysfs virtual filesystem. For every directory found in sysfs, there is a kobject lurking somewhere within the kernel. Every kobject of interest also exports one or more attributes, which appear in that kobject's sysfs directoryas files containing kernel-generated information. This section examines how kobjects and sysfs interact at a low level. 

Code that works with sysfs should include <linux/sysfs.h>.

Getting a kobject to show up in sysfs is simply a matter of calling kobject_add. We have already seen that function as the way to add a kobject to a kset; creating entries in sysfs is also part of its job. There are a couple of things worth knowing about how the sysfs entry is created:

  • Sysfs entries for kobjects are always directories, so a call to kobject_add results in the creation of a directory in sysfs.Usually that directory contains one or more attributes; we see how attributes are specified shortly.

  • The name assigned to the kobject (with kobject_set_name) is the name used for the sysfs directory. Thus, kobjects that appear in the same part of the sysfs hierarchymust have unique names. Names assigned to kobjects should also be reasonable file names: they cannot contain the slash character, and the use of white space is strongly discouraged.

  • The sysfs entry is located in the directory corresponding to the kobject's parent pointer. If parent is NULL when kobject_add is called, it is set to the kobject embedded in the new kobject's kset; thus, the sysfs hierarchy usually matches the internal hierarchy created with ksets. If both parent and kset are NULL, the sysfs directory is created at the top level, which is almost certainly not what you want.

Using the mechanisms we have described so far, we can use a kobject to create an empty directory in sysfs. Usually, you want to do something a little more interesting than that, so it is time to look at the implementation of attributes.

14.2.1. Default Attributes

When created, every kobject is given a set of default attributes. These attributes are specified by way of the kobj_type structure. That structure, remember, looks like this:

struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;
};

The default_attrs field lists the attributes to be created for every kobject of this type, and sysfs_ops provides the methods to implement those attributes. We start withdefault_attrs, which points to an array of pointers to attribute structures:

struct attribute {
    char *name;
    struct module *owner;
    mode_t mode;
};

In this structure, name is the name of the attribute (as it appears within the kobject's sysfs directory), owner is a pointer to the module (if any) that is responsible for the implementation of this attribute, and mode is the protection bits that are to be applied to this attribute. The mode is usually S_IRUGO for read-only attributes; if the attribute is writable, you can toss in S_IWUSR to give write access to root only (the macros for modes are defined in <linux/stat.h>). The last entry in the default_attrs list must be zero-filled.(mode其實是文件的權限,它包括三組:屬主權限U,屬組權限G,其他O,權限有r,w,x 如0666表示,三組UGO均可讀可寫,但不可執行)

The default_attrs array says what the attributes are but does not tell sysfs how to actually implement those attributes. That task falls to the kobj_type->sysfs_ops field, which points to a structure defined as:

struct sysfs_ops {
    ssize_t (*show)(struct kobject *kobj, struct attribute *attr, 
                    char *buffer);
    ssize_t (*store)(struct kobject *kobj, struct attribute *attr, 
                     const char *buffer, size_t size);
};

Whenever an attribute is read from user space, the show method is called with a pointer to the kobject and the appropriate attribute structure. That method should encode the value of the given attribute into buffer, being sure not to overrun it (it is PAGE_SIZE bytes), and return the actual length of the returned data. The conventions for sysfs state that each attribute should contain a single, human-readable value; if you have a lot of information to return, you may want to consider splitting it into multiple attributes.

The same show method is used for all attributes associated with a given kobject. The attr pointer passed into the function can be used to determine which attribute is being requested. Some show methods include a series of tests on the attribute name. Other implementations embed the attribute structure within another structure that contains the information needed to return the attribute's value; in this case, container_of may be used within the show method to obtain a pointer to the embedding structure.

The store method is similar; it should decode the data stored in buffer (size contains the length of that data, which does not exceed PAGE_SIZE), store and respond to the new value in whatever way makes sense, and return the number of bytes actually decoded. The store method can be called only if the attribute's permissions allow writes. When writing a store method, never forget that you are receiving arbitrary information from user space; you should validate it very carefully before taking any action in response. If the incoming data does not match expectations, return a negative error value rather than possibly doing something unwanted and unrecoverable. If your device exports aself_destruct attribute, you should require that a specific string be written there to invoke that functionality; an accidental, random write should yield only an error.(以上show,store操作與file_opertions關聯的實現流程是這樣的:當用戶空間open() -->sysfs_open_file()時,buffer->ops =  kobj_type->sysfs_opsfile->private_data = buffer;)

14.2.2. Nondefault Attributes

In many cases, the kobject type's default_attrs field describes all the attributes that kobject will ever have. But that's not a restriction in the design; attributes can be added and removed to kobjects at will. If you wish to add a new attribute to a kobject's sysfs directory, simply fill in an attribute structure and pass it to:(我們可以通過以下函數更改kobject的attibutes)

int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
類似的有:
int bus_create_file(struct bus_type *, struct bus_attribute *);
int driver_create_file(struct device_driver *, struct driver_attribute *);
device_create_file(dev,&dev_attr_power);
同樣atrribute也有struct bus_attribute,struct driver_attribute ,struct device_attribute.

If all goes well, the file is created with the name given in the attribute structure, and the return value is 0; otherwise, the usual negative error code is returned.

Note that the same show( ) and store( ) functions are called to implement operations on the new attribute. Before you add a new, nondefault attribute to a kobject, you should take whatever steps are necessary to ensure that those functions know how to implement that attribute.

To remove an attribute, call:

int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);

After the call, the attribute no longer appears in the kobject's sysfs entry. Do be aware, however, that a user-space process could have an open file descriptor for that attribute and that show and store calls are still possible after the attribute has been removed.

14.2.3. Binary Attributes

The sysfs conventions call for all attributes to contain a single value in a human-readable text format. That said, there is an occasional, rare need for the creation of attributes that can handle larger chunks of binary data. That need really only comes about when data must be passed, untouched, between user space and the device. For example, uploading firmware to devices requires this feature. When such a device is encountered in the system, a user-space program can be started (via the hotplug mechanism); that program then passes the firmware code to the kernel via a binary sysfs attribute, as is shown in Section 14.8.1.

Binary attributes are described with a bin_attribute structure:

struct bin_attribute {
    struct attribute attr;
    size_t size;
    ssize_t (*read)(struct kobject *kobj, char *buffer, 
                    loff_t pos, size_t size);
    ssize_t (*write)(struct kobject *kobj, char *buffer, 
                    loff_t pos, size_t size);
};

Here, attr is an attribute structure giving the name, owner, and permissions for the binary attribute, and size is the maximum size of the binary attribute (or 0 if there is no maximum). The read and write methods work similarly to the normal char driver equivalents; they can be called multiple times for a single load with a maximum of one page worth of data in each call. There is no way for sysfs to signal the last of a set of write operations, so code implementing a binary attribute must be able to determine the end of the data some other way.

Binary attributes must be created explicitly; they cannot be set up as default attributes. To create a binary attribute, call:

int sysfs_create_bin_file(struct kobject *kobj, 
                          struct bin_attribute *attr);

Binary attributes can be removed with:

int sysfs_remove_bin_file(struct kobject *kobj, 
                          struct bin_attribute *attr);

14.2.4. Symbolic Links

The sysfs filesystem has the usual tree structure, reflecting the hierarchical organization of the kobjects it represents. The relationships between objects in the kernel are often more complicated than that, however. For example, one sysfs subtree (/sys/devices) represents all of the devices known to the system, while other subtrees (under /sys/bus) represent the device drivers. These trees do not, however, represent the relationships between the drivers and the devices they manage. Showing these additional relationships requires extra pointers which, in sysfs, are implemented through symbolic links.

Creating a symbolic link within sysfs is easy:

int sysfs_create_link(struct kobject *kobj, struct kobject *target,
                      char *name);

This function creates a link (called name) pointing to target's sysfs entry as an attribute of kobj. It is a relative link, so it works regardless of where sysfs is mounted on any particular system.

The link persists even if target is removed from the system. If you are creating symbolic links to other kobjects, you should probably have a way of knowing about changes to those kobjects, or some sort of assurance that the target kobjects will not disappear. The consequences (dead symbolic links within sysfs) are not particularly grave, but they are not representative of the best programming style and can cause confusion in user space.

Symbolic links can be removed with:

void sysfs_remove_link(struct kobject *kobj, char *name);

創建一個kobject例程:

/*
 * Sample kobject implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <[email protected]>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a simple subdirectory in sysfs called
 * /sys/kernel/kobject-example  In that directory, 3 files are created:
 * "foo", "baz", and "bar".  If an integer is written to these files, it can be
 * later read out of it.
 */

static int foo;
static int baz;
static int bar;

/*
 * The "foo" file where a static variable is read from and written to.
 */
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf)
{
	return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count)
{
	sscanf(buf, "%du", &foo);
	return count;
}

static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0666, foo_show, foo_store);

/*
 * More complex function where we determine which varible is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
		      char *buf)
{
	int var;
	if (strcmp(attr->attr.name, "baz") == 0)
		var = baz;
	else
		var = bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
		       const char *buf, size_t count)
{
	int var;
	sscanf(buf, "%du", &var);
	if (strcmp(attr->attr.name, "baz") == 0)
		baz = var;
	else
		bar = var;
	return count;
}

static struct kobj_attribute baz_attribute =
	__ATTR(baz, 0666, b_show, b_store);
static struct kobj_attribute bar_attribute =
	__ATTR(bar, 0666, b_show, b_store);


/*
 * Create a group of attributes so that we can create and destory them all
 * at once.
 */
static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

/*
 * An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.
 */
static struct attribute_group attr_group = {
	.attrs = attrs,
};

static struct kobject *example_kobj;

static int __init example_init(void)
{
	int retval;

	/*
	 * Create a simple kobject with the name of "kobject_example",
	 * located under /sys/kernel/
	 *
	 * As this is a simple directory, no uevent will be sent to
	 * userspace.  That is why this function should not be used for
	 * any type of dynamic kobjects, where the name and number are
	 * not known ahead of time.
	 */
	example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
	if (!example_kobj)
		return -ENOMEM;
	

	/* Create the files associated with this kobject */
	retval = sysfs_create_group(example_kobj, &attr_group);
	if (retval)
		kobject_put(example_kobj);

	return retval;
}

static void __exit example_exit(void)
{
	kobject_put(example_kobj);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <[email protected]>");

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章