After a long period of development in Linux, many users are familiar with Linux Sysfs. I like Linux Sysfs very much. Let's talk about this in detail. Linux Sysfs source code analysis and programming practices. Linux is issued in accordance with the General Public License of GNUGNU is Not UNIX), the Public copyright License GPL, General Public License), adhering to the principle of "free thinking, open source code.
Understanding the use of Linux Sysfs attributes from the source code more functions of Linux Sysfs attributes can only be understood by reading the source code. We can still understand the scan attribute of scsi_host mentioned above. This function is not described in any document, so we can only read the source code.
In the kernel, the attributes of Linux Sysfs are generally declared by _ ATTR macros. For example, DEVICE_ATTR is used for devices, BUS_ATTR is used for bus, and DRIVER_ATTR is used for drivers, use CLASS_ATTR for a class. These four advanced macros come from Is from a lower layer The _ ATTR/_ ATRR_RO macro in is implemented. Therefore, we find the usage of these macros in the corresponding location drivers/scsi/in the kernel source code tree, in drivers/scsi/scsi_sysfs.c:
Static ssize_t
Store_scan (struct device * dev, struct device_attribute * attr,
Const char * buf, size_t count)
Struct Scsi_Host * shost = class_to_shost (dev );
Int res;
Res = scsi_scan (shost, buf );
If (res = 0)
Res = count;
Return res;
Static DEVICE_ATTR (scan, S_IWUSR, NULL, store_scan );
The DEVICE_ATTR macro has four parameters: name, permission bit, READ function, and write function. The corresponding name is scan. The permission is only for owner (S_IWUSR), no READ function, and only for writing function. Therefore, the read/write function corresponds to the permission bit, because DEVICE_ATTR puts the permission bit declaration together with whether the real read/write implementation reduces the possibility of inconsistency. (The permission bit declaration of the/proc/scsi interface mentioned above does not correspond to its function, which is related to the inconsistency in the function design for registering the proc interface, the permission bit declaration and function implementation are not in the same position in the code, so they are prone to errors. Although it is easy to fix/proc/scsi permission bit errors, no one in the kernel team has detected or not fixed this BUG for many years, it should be related to the expiration of/proc/scsi/interface, and outdated features will be removed in a future kernel version .)
The preceding scan attribute writing function is implemented in the store_scan function. Among the four parameters of this interface, buf/count represents the string written by the user. It further transmits the buf to the scsi_scan function; if you further analyze the implementation of the scsi_scan function, you can know that it expects to accept three or four integer values (also accept "-" As wildcards) from the buf, representing host, channel, three values: id (the fourth integer represents the lun number in the earlier kernel, but the fourth number is ignored in the newer kernel, and only accepts four integers as backward compatible reserved ), then, the specific host, channel, id is scanned again to find the device changes on the SCSI controller.
Add Linux Sysfs support
If the device driver you are developing needs interfaces with the user layer, the following methods are generally available:
Register a virtual character device file and use the read/write/ioctl and Other interfaces on the virtual device to interact with users. However, read/write can only do one thing, ioctl supports multiple functions based on cmd parameters, but its disadvantage is that the ioctl interface cannot be used directly in Shell scripts. To use the ioctl function, you must also compile a virtual device operating program for the C language. The binary data interface of ioctl also causes large and small endian problems (big endian and little endian), the root cause of the 32-bit/64-bit unportable problem;
Register the proc interface and accept the user's read/write/ioctl operations. Similarly, a proc usually uses its read/write/ioctl interface, its problems are similar to those of the above virtual character devices;
Register the Linux Sysfs attribute;
The most important thing is that the amount of code required to support adding virtual character devices and registering the proc interface is a lot. The best way is to support using the Linux Sysfs attribute, everything is visible and transparent on the user layer, with the least added code and the best maintainability. The method is to use The four macros provided by the header file are applied to four kernel data structure objects: Bus, category, driver, and device:
# Define BUS_ATTR (_ name, _ mode, _ show, _ store )\
Struct bus_attribute bus_attr _ # _ name = _ ATTR (_ name, _ mode, _ show, _ store)
# Define CLASS_ATTR (_ name, _ mode, _ show, _ store )\
Struct class_attribute class_attr _ # _ name = _ ATTR (_ name, _ mode, _ show, _ store)
# Define DRIVER_ATTR (_ name, _ mode, _ show, _ store )\
Struct driver_attribute driver_attr _ # _ name = \
_ ATTR (_ name, _ mode, _ show, _ store)
# Define DEVICE_ATTR (_ name, _ mode, _ show, _ store )\
Struct device_attribute dev_attr _ # _ name = _ ATTR (_ name, _ mode, _ show, _ store)
BUS and CLASS attributes are generally used for newly designed BUS and newly designed CLASS, which are generally not used; because your device is generally connected to the host in a mature way such as PCI, and there is no new type. The difference between using the driver property and the device property is: check whether your Linux Sysfs attribute design is effective for the entire driver or for each device that the driver may support.
You can also find the prototype of the show/store function from the original file. It is similar to the read/write function of the virtual character device or proc item, however, the difference is that the buf/count parameter in the show/store function has been used for memory replication in the user zone/kernel zone on the Linux Sysfs layer, the common _ user attribute on a virtual character device is not required here, so you do not need to copy _from_user/copy_to_user once more, the buf/count parameter on the show/store function parameter is already the address of the kernel zone and can be operated directly.
The above four are the advanced interfaces added to the Linux Sysfs unified device model. If you use the underlying interfaces provided by Linux Sysfs, there are two other interfaces defined from :( The above four interfaces of bus/category/driver/device are implemented by _ ATTR here)
# Define _ ATTR (_ name, _ mode, _ show, _ store ){\
. Attr = {. name = _ stringify (_ name),. mode = _ mode },\
. Show = _ show ,\
. Store = _ store ,\
# Define _ ATTR_RO (_ name ){\
. Attr = {. name = _ stringify (_ name),. mode = 0444 },\
. Show = _ name ##_ show ,\
These macros are used as default attributes when registering bus/category/driver/device. in actual application, another situation is to dynamically add attributes based on conditions, for example, resource {0, 1, 2,...} On the PCI device ,...} attribute file, because the maximum number of able resources on a PCI device is unpredictable, and can only be dynamically added as a condition.
Int _ must_check sysfs_create_file (struct kobject * kobj,
Const struct attribute * attr );
Int _ must_check sysfs_create_bin_file (struct kobject * kobj, struct bin_attribute * attr );
These two functions can dynamically add text or binary attributes to a kobject. This is the only way to add binary attributes.
The difference between a Binary attribute and a common text attribute is that a struct attribute struct object is embedded in the Binary attribute struct bin_attribute, so it has all functional features of a common attribute; an extra size in the binary attribute is used to describe the size of the binary file, while the size of the common attribute file is always 4096. To be precise, it should be the size of a memory page, in the current Linux Sysfs kernel implementation, it allocates a memory page to serve as a buffer (buf/count); binary attributes support more memory ing (mmap) interfaces than common attributes;
Programming example: Linux Sysfs improvement for the LDD3 driver
First, this program was written for the kernel (2.6.11) of the time when the author wrote the book, in the current Fedora10 System (2.6.27.5-117. fc10.i686) can not be compiled or compiled. Therefore, you need to first port it to a running state at least. The compressed package of the attachment contains the modified ld.pdf, the source code of sculld and the four patches in the modification process:
The first 0001-ldd3-examples-build-on-fedora-10-2.6.27.5-117.fc10. I .patch is to port ld.pdf and sculld TO THE Fedora10 kernel for running, which is mainly a change in the kernel API;
The second 0002-port-dmem-proc-entry-to-use-sysfs-entry.patch demonstrates how to improve the original proc interface to the Linux Sysfs attribute interface. From this patch, you can see that the deleted code is too large and the new Code is less, this indicates that for the same function, the number of codes using the Linux Sysfs programming interface is less, and the sysfs code looks more neat than proc: you can print the debugging information of each device so that each device has its own interface instead of a unified proc interface; the final location of the device property file is as follows: "/sys/devices/ldd0/sculld0/F8"; static ssize_t sculld_show_f8 (struct device * ddev,
Struct device_attribute * attr, char * buf)
/* The code that prints debugging information for each device is copied from the original proc interface */
Static DEVICE_ATTR (maid, S_IRUGO, sculld_show_dmem, NULL );
Static int _ init sculld_register_dev (struct sculld_dev * dev, int index)
/* Create this device property file */
Ret | = device_create_file (& dev-> ldev. dev, & dev_attr_f8 );
The third 0003-add-. gitignore. patch adds the. gitignore file and shields some temporary files generated by compilation;
The fourth 0004-port-qset-get-set-ioctl-to-use-sysfs-entry.patch demonstrates how to improve the ioctl-based operation interface to the Linux Sysfs interface. Because the original ioctl interface sets and obtains the qset information, it indicates the entire driver module-level variables, it is used to control the entire driver rather than a single device supported by the driver, so this qset attribute is more suitable to add using DRIVER_ATTR; ssize_t sculld_show_qset (struct device_driver * driver, char * buf)
Return snprintf (buf, PAGE_SIZE, "% d \ n", sculld_qset );
Ssize_t sculld_store_qset (struct device_driver * driver, const char * buf,
Size_t count) sculld_qset = simple_strtol (buf, NULL, 0); return count;
/* Declare a 0644 concurrent read/write driver attribute */
Static DRIVER_ATTR (qset, S_IRUGO | S_IWUSR, sculld_show_qset, sculld_store_qset );
/* Create this driver property file */
Result = driver_create_file (& sculld_driver.driver, & driver_attr_qset );
The driver attributes eventually appear, for example, "/sys/bus/ldd/drivers/sculld/qset". Here, the declared attributes are both readable and writable, and the permission bit 0644 is consistent with that of the driver. 6446 0-rw-r -- 1 root 4096 December 14 07:44/sys/bus/ldd/drivers/sculld/qset
- Analyze the Linux and Windows operating systems for you
- Describes the hardware supported by Linux operating system installation and recognition.
- Details about how to use Windows partitions in Linux
- Linux technology is getting better than NEC
- Diverse Linux desktops