feat(MP/kernel): create sysfs to contains module attributs
- use callback to update sysfs with the core
This commit is contained in:
141
src/06-mini-project/kernel/sysfs/sysfs.c
Normal file
141
src/06-mini-project/kernel/sysfs/sysfs.c
Normal file
@@ -0,0 +1,141 @@
|
||||
// workspace/src/06-mini-project/kernel/sysfs/sysfs.c
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "sysfs.h"
|
||||
|
||||
static struct class *sysfs_class = NULL;
|
||||
static struct device *sysfs_device = NULL;
|
||||
|
||||
/* Global static structure to hold the registered callbacks */
|
||||
static struct sysfs_callbacks device_cbs = {0};
|
||||
|
||||
/* Callback triggered on sysfs temperature file read */
|
||||
static ssize_t temperature_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
uint32_t temp = 0;
|
||||
|
||||
if (device_cbs.get_temperature) {
|
||||
temp = device_cbs.get_temperature();
|
||||
}
|
||||
|
||||
/* Format the temperature and write it to the buffer */
|
||||
return snprintf(buf, PAGE_SIZE, "%u.%03u\n", temp / 1000, temp % 1000);
|
||||
}
|
||||
|
||||
/* Callback triggered on sysfs mode file read */
|
||||
static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
int mode = 0;
|
||||
|
||||
if (device_cbs.get_mode) {
|
||||
mode = device_cbs.get_mode();
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", mode);
|
||||
}
|
||||
|
||||
/* Callback triggered on sysfs mode file write */
|
||||
static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
|
||||
int mode;
|
||||
|
||||
/* Safely convert string from user space to integer */
|
||||
if (kstrtoint(buf, 10, &mode) == 0) {
|
||||
if (device_cbs.set_mode) {
|
||||
device_cbs.set_mode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Callback triggered on sysfs period_status file read */
|
||||
static ssize_t period_status_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
uint32_t period = 0;
|
||||
|
||||
if (device_cbs.get_period) {
|
||||
period = device_cbs.get_period();
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", period);
|
||||
}
|
||||
|
||||
/* Callback triggered on sysfs period_set file write */
|
||||
static ssize_t period_set_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
|
||||
uint32_t period;
|
||||
|
||||
if (kstrtouint(buf, 10, &period) == 0) {
|
||||
if (device_cbs.set_period) {
|
||||
device_cbs.set_period(period);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Create the sysfs attribute structures with proper permissions */
|
||||
static DEVICE_ATTR_RO(temperature);
|
||||
static DEVICE_ATTR_RW(mode);
|
||||
static DEVICE_ATTR_RO(period_status);
|
||||
static DEVICE_ATTR_WO(period_set);
|
||||
|
||||
/* Group all attributes together to register them efficiently */
|
||||
static struct attribute *regulator_attrs[] = {
|
||||
&dev_attr_temperature.attr,
|
||||
&dev_attr_mode.attr,
|
||||
&dev_attr_period_status.attr,
|
||||
&dev_attr_period_set.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group regulator_group = {
|
||||
.attrs = regulator_attrs,
|
||||
};
|
||||
|
||||
int temp_regulator_sysfs_init(struct sysfs_callbacks *cbs) {
|
||||
int ret;
|
||||
|
||||
/* Save the callbacks provided by the main module */
|
||||
if (cbs) {
|
||||
device_cbs = *cbs;
|
||||
}
|
||||
|
||||
/* Create sysfs class */
|
||||
sysfs_class = class_create(THIS_MODULE, "temp_regulator");
|
||||
if (IS_ERR(sysfs_class)) {
|
||||
return PTR_ERR(sysfs_class);
|
||||
}
|
||||
|
||||
/* Create sysfs device */
|
||||
sysfs_device = device_create(sysfs_class, NULL, MKDEV(0, 0), NULL, "regulator");
|
||||
if (IS_ERR(sysfs_device)) {
|
||||
class_destroy(sysfs_class);
|
||||
return PTR_ERR(sysfs_device);
|
||||
}
|
||||
|
||||
/* Register all files in the sysfs directory at once */
|
||||
ret = sysfs_create_group(&sysfs_device->kobj, ®ulator_group);
|
||||
if (ret) {
|
||||
device_destroy(sysfs_class, MKDEV(0, 0));
|
||||
class_destroy(sysfs_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void temp_regulator_sysfs_exit(void) {
|
||||
/* Clean up sysfs files and destroy resources */
|
||||
if (sysfs_device != NULL) {
|
||||
sysfs_remove_group(&sysfs_device->kobj, ®ulator_group);
|
||||
device_destroy(sysfs_class, MKDEV(0, 0));
|
||||
sysfs_device = NULL;
|
||||
}
|
||||
|
||||
if (sysfs_class != NULL) {
|
||||
class_destroy(sysfs_class);
|
||||
sysfs_class = NULL;
|
||||
}
|
||||
}
|
||||
24
src/06-mini-project/kernel/sysfs/sysfs.h
Normal file
24
src/06-mini-project/kernel/sysfs/sysfs.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// workspace/src/06-mini-project/kernel/sysfs/sysfs.h
|
||||
#ifndef SYSFS_H
|
||||
#define SYSFS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Structure holding all the necessary callbacks to interact with the hardware */
|
||||
struct sysfs_callbacks {
|
||||
uint32_t (*get_temperature)(void);
|
||||
|
||||
int (*get_mode)(void);
|
||||
void (*set_mode)(int mode);
|
||||
|
||||
uint32_t (*get_period)(void);
|
||||
void (*set_period)(uint32_t freq);
|
||||
};
|
||||
|
||||
/* Register the callbacks and create the sysfs interface */
|
||||
int temp_regulator_sysfs_init(struct sysfs_callbacks *cbs);
|
||||
|
||||
/* Remove sysfs files and destroy device and class */
|
||||
void temp_regulator_sysfs_exit(void);
|
||||
|
||||
#endif /* SYSFS_H */
|
||||
Reference in New Issue
Block a user