I'm learning how to use sysfs in my Linux modules, but I'm having the hardest time finding current documentation on these topics. The Linux Device Drivers 3rd Edition book I've been using seems to be rather dated in this area unfortunately (e.g. the class_device
structure appears to be completely gone in current Linux versions).
I'm simply trying to get an attribute to appear, under the respective sysfs class for my module, that will allow me to read the value of a module variable from kernel space.
In my code, I have a class created that allows udev to create a device node at /dev/foo for my module:
dev_t foo_dev;
alloc_chrdev_region(&foo_dev, 0, 1, "bar");
struct class *bar = class_create(THIS_MODULE, "bar");
device_create(bar, NULL, foo_dev, NULL, "foo");
struct cdev foo_dev_file;
cdev_init(&foo_dev_file, &fops); /* fops defined earlier */
cdev_add(&foo_dev_file, foo_dev, 1);
When I insert the module I get a sysfs class directory created and populated with some default attributes at /sys/class/bar/foo/. How can I create attributes that show up under this new directory?
I have the concepts down pretty well I believe -- create attribute structure, define sysfs_ops
functions, etc -- my problem is that I don't know which particular kernel structure to use (class_attribute
?), nor how to make these attributes appear under the right sysfs directory.
Would anyone point me to a tutorial or article detailing the process for current Linux kernels?
int sysfs_create_file(struct kobject *kobj, struct attribute *attr); This function accepts kboject structure, attributes and using these it creates directory in sysfs. I understood that at the time of boot, kernel detects the memory and creates the directories under sys/devices/system/memory.
sysfs is a pseudo file system provided by the Linux kernel that exports information about various kernel subsystems, hardware devices, and associated device drivers from the kernel's device model to user space through virtual files.
Even though my knowledge is still fairly low on the topic, I'm going to post an answer just because of the age of this question. If somebody else has a better answer, please post! :)
First off, I'm going to assume that you've read that whole chapter (specifically about kobjects & ksets). So just about every struct in the device driver model has these cutely included in them. If you want to manipulate the kobject for the class its self (not sure if that's wise or not), that's your struct class
's dev_kobj
member.
However, you want to manipulate the attributes of that class. I believe you do this by defining a (usually static), NULL-terminated array of them as follows and then assigning its address to the struct class
's class_attrs
member (taken from drivers/uwb/driver.c
):
static struct class_attribute uwb_class_attrs[] = {
__ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO,
beacon_timeout_ms_show, beacon_timeout_ms_store),
__ATTR_NULL,
};
/** Device model classes */
struct class uwb_rc_class = {
.name = "uwb_rc",
.class_attrs = uwb_class_attrs,
};
When I don't know how to use something, I usually git grep
the repository for somebody else who has used it and try to learn from it that way. It would seem that this is why they tend to say kernel "hackers" and not "developers".
Minimal runnable example
Usage:
insmod /sysfs.ko
cd /sys/kernel/lkmc_sysfs
printf 12345 >foo
cat foo
# => 1234
dd if=foo bs=1 count=2 skip=1 status=none
# => 23
sysfs.c
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <uapi/linux/stat.h> /* S_IRUSR, S_IWUSR */
enum { FOO_SIZE_MAX = 4 };
static int foo_size;
static char foo_tmp[FOO_SIZE_MAX];
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buff)
{
strncpy(buff, foo_tmp, foo_size);
return foo_size;
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buff, size_t count)
{
foo_size = min(count, (size_t)FOO_SIZE_MAX);
strncpy(foo_tmp, buff, foo_size);
return count;
}
static struct kobj_attribute foo_attribute =
__ATTR(foo, S_IRUGO | S_IWUSR, foo_show, foo_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *kobj;
static int myinit(void)
{
int ret;
kobj = kobject_create_and_add("lkmc_sysfs", kernel_kobj);
if (!kobj)
return -ENOMEM;
ret = sysfs_create_group(kobj, &attr_group);
if (ret)
kobject_put(kobj);
return ret;
}
static void myexit(void)
{
kobject_put(kobj);
}
module_init(myinit);
module_exit(myexit);
MODULE_LICENSE("GPL");
GitHub upstream.
Tested with Linux kernel 5.0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With