sysfs ===== .. tab-set:: .. tab-item:: sysfs * In this program, you are going to learn .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to Communicate between the Userspace and Kernel Space ? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use below APIs ? * `kobject_create_and_add `_ * `sysfs_create_file `_ * `kobject_put `_ * `sysfs_remove_file `_ .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`Explanation of program part by part ` * :ref:`List of headers ` * :ref:`Module macros ` * :ref:`Initialize variables and functions ` * :ref:`Module init function ` * :ref:`Module exit function ` * :ref:`Add the sysfs APIs ` * :ref:`Mention init and exit functions ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load ` * :ref:`Summary of sysfs APIs ` .. _p1_sysfs_4: .. tab-set:: .. tab-item:: Explanation of program part by part * Sysfs is the commonly used method to export system information from the kernel space to the user space for specific devices. Here is the explanation of the program. .. _p1_sysfs_5: .. tab-set:: .. tab-item:: List of headers * Add the list of headers to refer the APIs used in the source code. .. code-block:: c #include #include #include #include #include #include #include #include #include #include #include #include .. _p1_sysfs_6: .. tab-set:: .. tab-item:: Modules macros * Add the module macros which contains the information about macros such as author, description and license. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux_usr"); MODULE_DESCRIPTION("Simple Linux Device Driver - sysfs"); .. _p1_sysfs_7: .. tab-set:: .. tab-item:: Initialize function and variables * Initialize the functions and the variables which are used in the source code. .. code-block:: c dev_t dev; static struct class *dev_class; static struct cdev etx_cdev; struct kobject *kobj_ref; static int __init etx_driver_init(void); static void __exit etx_driver_exit(void); static int etx_open(struct inode *inode, struct file *file); static int etx_release(struct inode *inode, struct file *file); static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off); static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t *off); static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); struct kobj_attribute etx_attr = __ATTR(etx_value, 0660, sysfs_show, sysfs_store); static const struct file_operations fops = { .owner = THIS_MODULE, .read = etx_read, .write = etx_write, .open = etx_open, .release = etx_release, }; .. _p1_sysfs_8: .. tab-set:: .. tab-item:: Module init functions * Add the module init function to execute once when the module is loaded. .. code-block:: c static int __init etx_driver_init(void) { /*Allocating Major number*/ if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0) { pr_info("Cannot allocate major number\n"); return -1; } pr_info("Major = %d Minor = %d\n", MAJOR(dev), MINOR(dev)); /*Creating cdev structure*/ cdev_init(&etx_cdev, &fops); /*Adding character device to the system*/ if ((cdev_add(&etx_cdev, dev, 1)) < 0) { pr_info("Cannot add the device to the system\n"); goto r_class; } /*Creating struct class*/ dev_class = class_create(THIS_MODULE, "etx_class"); if (IS_ERR(dev_class)) { pr_info("Cannot create the struct class\n"); goto r_class; } /*Creating device*/ if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "etx_device"))) { pr_info("Cannot create the Device 1\n"); goto r_device; } /*Creating a directory in /sys/kernel/ */ kobj_ref = kobject_create_and_add("etx_sysfs", kernel_kobj); /*Creating sysfs file for etx_value*/ if (sysfs_create_file(kobj_ref, &etx_attr.attr)) { pr_err("Cannot create sysfs file......\n"); goto r_sysfs; } pr_info("Device Driver Insert...Done!!!\n"); return 0; r_sysfs: kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &etx_attr.attr); r_device: class_destroy(dev_class); r_class: unregister_chrdev_region(dev, 1); cdev_del(&etx_cdev); return -1; } * ``kobject_create_and_add`` this function creates a kobject structure dynamically and registers it with sysfs. For example, .. code-block:: c kobj_ref = kobject_create_and_add("etx_sysfs", kernel_kobj); * If you pass kernel_kobj to the second argument, it will create the directory under /sys/kernel/. If you pass firmware_kobj to the second argument, it will create the directory under /sys/firmware/. If you pass fs_kobj to the second argument, it will create the directory under /sys/fs/. If you pass NULL to the second argument, it will create the directory under /sys/. * ``sysfs_create_file`` is used to create a single file attribute. For example, .. code-block:: c sysfs_create_file(kobj_ref, &etx_attr.attr); * ``kobject_put`` is a function used to decrease the reference count of a kernel object and potentially release associated resources. For example, .. code-block:: c kobject_put(kobj_ref); .. _p1_sysfs_9: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which is executed when the module is unloaded from the kernel. .. code-block:: c static void __exit etx_driver_exit(void) { kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &etx_attr.attr); device_destroy(dev_class, dev); class_destroy(dev_class); cdev_del(&etx_cdev); unregister_chrdev_region(dev, 1); pr_info("Device Driver Remove...Done!!!\n"); } * ``sysfs_remove_file`` is a function used to remove a sysfs file associated with a particular kernel object. For example, .. code-block:: c sysfs_remove_file(kernel_kobj, &etx_attr.attr); .. _p1_sysfs_11: .. tab-set:: .. tab-item:: sysfs APIs * Add the sysfs APIs. .. code-block:: c static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { pr_info("Sysfs - Read!!!\n"); return sprintf(buf, "%d", etx_value); } static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { pr_info("Sysfs - Write!!!\n"); sscanf(buf, "%d", &etx_value); return count; } // This function will be called when we open the Device file static int etx_open(struct inode *inode, struct file *file) { pr_info("Device File Opened...!!!\n"); return 0; } // This function will be called when we close the Device file static int etx_release(struct inode *inode, struct file *file) { pr_info("Device File Closed...!!!\n"); return 0; } // This function will be called when we read the Device file static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off) { pr_info("Read function\n"); return 0; } // This function will be called when we write the Device file static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) { pr_info("Write Function\n"); return len; } .. _p1_sysfs_10: .. tab-set:: .. tab-item:: Mention the module init and exit function * Add the mention init and exit function which is executed the module is loaded and unloaded. .. code-block:: c module_init(etx_driver_init); module_exit(etx_driver_exit); .. _p1_sysfs_0: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: driver_sysfs.c .. literalinclude:: p1_sysfs/driver_sysfs.c :language: c :linenos: .. _p1_sysfs_1: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p1_sysfs/Makefile :language: c :linenos: .. _p1_sysfs_2: .. tab-set:: .. tab-item:: Compile and Load * Run Make to compile the module. .. code-block:: shell $make make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_driver_sysfs modules make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic' CC [M] $HOME/kernel_driver_sysfs/driver_sysfs.o Building modules, stage 2. MODPOST 1 modules CC [M] $HOME/kernel_driver_sysfs/driver_sysfs.mod.o LD [M] $HOME/kernel_driver_sysfs/driver_sysfs.ko make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic' * Run ls to check if driver_sysfs.ko is generated or not. .. code-block:: shell $ ls -l total 36 -rw-rw-r-- 1 test test 154 Feb 26 13:18 Makefile -rw-rw-r-- 1 test test 47 Feb 26 13:18 modules.order -rw-rw-r-- 1 test test 0 Feb 26 13:18 Module.symvers -rw-rw-r-- 1 test test 820 Feb 26 13:18 driver_sysfs.c -rw-rw-r-- 1 test test 5880 Feb 26 13:18 driver_sysfs.ko -rw-rw-r-- 1 test test 47 Feb 26 13:18 driver_sysfs.mod -rw-rw-r-- 1 test test 919 Feb 26 13:18 driver_sysfs.mod.c -rw-rw-r-- 1 test test 3448 Feb 26 13:18 driver_sysfs.mod.o -rw-rw-r-- 1 test test 3320 Feb 26 13:18 driver_sysfs.o * Run insmod to load the module. .. code-block:: shell $ sudo insmod ./driver_sysfs.ko * Once the module is loaded into kernel do read and write operations to sysfs. .. code-block:: shell $ ls -l /sys/kernel/etx_sysfs/ total 0 -rw-rw---- 1 root root 4096 Jul 14 18:46 etx_value $ cat /sys/kernel/etx_sysfs/etx_value 0 $ echo 123 > /sys/kernel/etx_sysfs/etx_value $ cat /sys/kernel/etx_sysfs/etx_value 123 * Run rmmod to unload the module. .. code-block:: shell $ sudo rmmod driver_sysfs * Check dmesg and we can clearly see the read and write operations are executed. .. code-block:: shell [ 3598.908616] Major = 239 Minor = 0 [ 3598.908694] Device Driver Insert...Done!!! [ 3665.394581] Sysfs - Read!!! [ 3680.240953] Sysfs - Write!!! [ 3696.011364] Sysfs - Read!!! [ 3705.496764] Device Driver Remove...Done!!! .. _p1_sysfs_3: .. tab-set:: .. tab-item:: Summary =============================== ================================================================= API Learning =============================== ================================================================= kobject_create_and_add To creates a kobject structure sysfs_create_file To create a single file attribute kobject_put To decrease the reference count of a kernel object sysfs_remove_file To remove a sysfs file associated with a particular kernel object =============================== ================================================================= .. card:: See Also * Previous Chapters * :doc:`../chapter1_basics/chapter1_basics` * :doc:`../chapter2_kthreads/chapter2_kthreads` * :doc:`../chapter3_tasklets/chapter3_tasklets` * :doc:`../chapter4_workqueue/chapter4_workqueue` * :doc:`../chapter5_timer/chapter5_timer` * :doc:`../chapter8_linkedList/chapter8_linkedList` * Other File System topics * :doc:`p2_procfs` * :doc:`p3_debugfs` * Next Chapter * :doc:`../chapter10_netlink/chapter10_netlink`