sysfs
In this program, you are going to learn
How to Communicate between the Userspace and Kernel Space ?
How to use below APIs ?
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 |
Previous Chapters
Other File System topics
Next Chapter