访问设备有两种途径: 一种是创建/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 OperationsKobjects 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:
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 AttributesWhen 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_ops; file->private_data = buffer;) 14.2.2. Nondefault AttributesIn 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 AttributesThe 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 LinksThe 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]>");