sysfs

  • In this program, you are going to learn

  • How to Communicate between the Userspace and Kernel Space ?

  • 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.

  • Add the list of headers to refer the APIs used in the source code.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/err.h>
  • Add the module macros which contains the information about macros such as author, description and license.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Simple Linux Device Driver - sysfs");
  • Initialize the functions and the variables which are used in the source code.

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,
};
  • Add the module init function to execute once when the module is loaded.

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,

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,

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,

kobject_put(kobj_ref);
  • Add the module exit function which is executed when the module is unloaded from the kernel.

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,

sysfs_remove_file(kernel_kobj, &etx_attr.attr);
  • Add the sysfs APIs.

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;
}
  • Add the mention init and exit function which is executed the module is loaded and unloaded.

module_init(etx_driver_init);
module_exit(etx_driver_exit);
  1#include <linux/kernel.h>
  2#include <linux/init.h>
  3#include <linux/module.h>
  4#include <linux/kdev_t.h>
  5#include <linux/fs.h>
  6#include <linux/cdev.h>
  7#include <linux/device.h>
  8#include <linux/slab.h>              //kmalloc()
  9#include <linux/uaccess.h>             //copy_to/from_user()
 10#include <linux/sysfs.h>
 11#include <linux/kobject.h>
 12#include <linux/err.h>
 13
 14volatile int etx_value;
 15
 16MODULE_LICENSE("GPL");
 17MODULE_AUTHOR("Linux_usr");
 18MODULE_DESCRIPTION("Simple Linux Device Driver - sysfs");
 19
 20dev_t dev;
 21static struct class *dev_class;
 22static struct cdev etx_cdev;
 23struct kobject *kobj_ref;
 24
 25
 26static int      __init etx_driver_init(void);
 27static void     __exit etx_driver_exit(void);
 28
 29static int      etx_open(struct inode *inode, struct file *file);
 30static int      etx_release(struct inode *inode, struct file *file);
 31static ssize_t  etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off);
 32static ssize_t  etx_write(struct file *filp, const char *buf, size_t len, loff_t *off);
 33
 34
 35static ssize_t  sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
 36static ssize_t  sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);
 37struct kobj_attribute etx_attr = __ATTR(etx_value, 0660, sysfs_show, sysfs_store);
 38
 39
 40static const struct file_operations fops = {
 41	.owner          = THIS_MODULE,
 42	.read           = etx_read,
 43	.write          = etx_write,
 44	.open           = etx_open,
 45	.release        = etx_release,
 46};
 47
 48
 49static ssize_t sysfs_show(struct kobject *kobj,	struct kobj_attribute *attr, char *buf)
 50{
 51	pr_info("Sysfs - Read!!!\n");
 52	return sprintf(buf, "%d", etx_value);
 53}
 54
 55
 56static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
 57{
 58	pr_info("Sysfs - Write!!!\n");
 59	sscanf(buf, "%d", &etx_value);
 60	return count;
 61}
 62
 63// This function will be called when we open the Device file
 64
 65static int etx_open(struct inode *inode, struct file *file)
 66{
 67	pr_info("Device File Opened...!!!\n");
 68	return 0;
 69}
 70
 71// This function will be called when we close the Device file
 72
 73static int etx_release(struct inode *inode, struct file *file)
 74{
 75	pr_info("Device File Closed...!!!\n");
 76	return 0;
 77}
 78
 79
 80// This function will be called when we read the Device file
 81
 82static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
 83{
 84	pr_info("Read function\n");
 85	return 0;
 86}
 87
 88// This function will be called when we write the Device file
 89
 90static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
 91{
 92	pr_info("Write Function\n");
 93	return len;
 94}
 95
 96
 97static int __init etx_driver_init(void)
 98{
 99	/*Allocating Major number*/
100	if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0) {
101		pr_info("Cannot allocate major number\n");
102		return -1;
103	}
104	pr_info("Major = %d Minor = %d\n", MAJOR(dev), MINOR(dev));
105
106	/*Creating cdev structure*/
107	cdev_init(&etx_cdev, &fops);
108
109	/*Adding character device to the system*/
110	if ((cdev_add(&etx_cdev, dev, 1)) < 0) {
111		pr_info("Cannot add the device to the system\n");
112		goto r_class;
113	}
114
115	/*Creating struct class*/
116	dev_class = class_create(THIS_MODULE, "etx_class");
117	if (IS_ERR(dev_class)) {
118		pr_info("Cannot create the struct class\n");
119		goto r_class;
120	}
121
122	/*Creating device*/
123	if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "etx_device"))) {
124		pr_info("Cannot create the Device 1\n");
125		goto r_device;
126	}
127
128	/*Creating a directory in /sys/kernel/ */
129	kobj_ref = kobject_create_and_add("etx_sysfs", kernel_kobj);
130
131	/*Creating sysfs file for etx_value*/
132	if (sysfs_create_file(kobj_ref, &etx_attr.attr)) {
133		pr_err("Cannot create sysfs file......\n");
134		goto r_sysfs;
135	}
136	pr_info("Device Driver Insert...Done!!!\n");
137	return 0;
138
139r_sysfs:
140	kobject_put(kobj_ref);
141	sysfs_remove_file(kernel_kobj, &etx_attr.attr);
142
143r_device:
144	class_destroy(dev_class);
145r_class:
146	unregister_chrdev_region(dev, 1);
147	cdev_del(&etx_cdev);
148	return -1;
149}
150
151
152static void __exit etx_driver_exit(void)
153{
154	kobject_put(kobj_ref);
155	sysfs_remove_file(kernel_kobj, &etx_attr.attr);
156	device_destroy(dev_class, dev);
157	class_destroy(dev_class);
158	cdev_del(&etx_cdev);
159	unregister_chrdev_region(dev, 1);
160	pr_info("Device Driver Remove...Done!!!\n");
161}
162
163module_init(etx_driver_init);
164module_exit(etx_driver_exit);
1obj-m += driver_sysfs.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run Make to compile the module.

$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.

$ 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.

$ sudo insmod ./driver_sysfs.ko
  • Once the module is loaded into kernel do read and write operations to sysfs.

$ 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.

$ sudo rmmod driver_sysfs
  • Check dmesg and we can clearly see the read and write operations are executed.

[ 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!!!

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

See Also