Linux Device Drivers (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Linux Device Drivers (3rd Edition) [Electronic resources] - نسخه متنی

Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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 directory as 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
    hierarchy must 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 with
default_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.

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 a
self_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.


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:

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

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);


    / 202