160 lines
3.8 KiB
C
160 lines
3.8 KiB
C
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/printk.h>
|
|
#define st_debug(format, ...) \
|
|
pr_debug("(file:%s,line:%d,pid:%d): " format, \
|
|
__FILE__, __LINE__, \
|
|
current->pid, ##__VA_ARGS__)
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/cdev.h>
|
|
|
|
#define STUDENT_CHRDEV "marvin-the-robot"
|
|
#define MAX_VALUES 3
|
|
|
|
static int student_chrdev_major = 0;
|
|
static struct device *my_dev = NULL;
|
|
|
|
typedef struct student_internal {
|
|
int values[MAX_VALUES];
|
|
int idx;
|
|
} st_int_t;
|
|
static st_int_t *data = NULL;
|
|
|
|
#if 0
|
|
static st_int_t* get_priv_data(struct file *file) {
|
|
return (st_int_t*)file->private_data;
|
|
}
|
|
|
|
static void set_priv_data(struct file *file, st_int_t *data) {
|
|
file->private_data = data;
|
|
}
|
|
#endif
|
|
|
|
static int sum(int val[], int len) {
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
ret += val[i];
|
|
}
|
|
return ret + CONFIG_STUDENT_OFFSET;
|
|
}
|
|
|
|
static ssize_t student_read(struct file *file, char __user *buf, size_t count,
|
|
loff_t *ppos) {
|
|
size_t len = 0;
|
|
char tmp[10];
|
|
|
|
if (!data) {
|
|
st_debug("No calculation started. Doing nothing.\n");
|
|
return 0;
|
|
}
|
|
|
|
len = snprintf(tmp, 10, "%d\n", sum(data->values, MAX_VALUES));
|
|
st_debug("About to copy %lu bytes to the userspace\n", len);
|
|
len = min(len, count);
|
|
if (count < len) {
|
|
pr_err("Sorry, your buffer is %lu bytes too small.\n", len - count);
|
|
}
|
|
if (copy_to_user(buf, tmp, len)) {
|
|
pr_err("User copy failed!\n");
|
|
return -EFAULT;
|
|
}
|
|
// Arthur Dent just likes to read the answer to the ultimate question, the Universe, and everything: 42!
|
|
st_debug("Copied %lu bytes to the userspace\n", len);
|
|
data = NULL;
|
|
st_debug("Resetted internal state\n");
|
|
*ppos += len;
|
|
return len;
|
|
}
|
|
|
|
static ssize_t student_write(struct file *file, const char __user *buf, size_t count,
|
|
loff_t *ppos) {
|
|
char *buf_copy;
|
|
int tmp;
|
|
|
|
buf_copy = memdup_user_nul(buf, count);
|
|
if (IS_ERR(buf_copy)) {
|
|
return PTR_ERR(buf_copy);
|
|
}
|
|
if (kstrtos32(buf_copy, 0, &tmp) != 0) {
|
|
kfree(buf_copy);
|
|
return -EINVAL;
|
|
}
|
|
kfree(buf_copy);
|
|
st_debug("New value read: %d\n", tmp);
|
|
if (data == NULL) {
|
|
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
|
if (!data) {
|
|
return -ENOMEM;
|
|
}
|
|
data->idx = 0;
|
|
for (int i = 0; i < MAX_VALUES; i++) {
|
|
data->values[i] = 0;
|
|
}
|
|
st_debug("Adding newly allocation st_int_t to priv_data: 0x%lx\n", (uintptr_t)data);
|
|
}
|
|
if (data->idx < MAX_VALUES) {
|
|
data->values[data->idx] = tmp;
|
|
st_debug("New value (%d) add at idx %d\n", tmp, data->idx);
|
|
data->idx++;
|
|
} else {
|
|
return -ENOMEM;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
const struct file_operations student_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = student_read,
|
|
.write = student_write,
|
|
};
|
|
|
|
static struct class universe_class = {
|
|
.name = STUDENT_CHRDEV,
|
|
};
|
|
|
|
static int __init student_chrdevinit(void) {
|
|
int err;
|
|
|
|
student_chrdev_major = err = register_chrdev(0, STUDENT_CHRDEV, &student_fops);
|
|
if (err < 0) {
|
|
pr_err("Cannot register chrdev: %d\n", err);
|
|
goto out;
|
|
}
|
|
err = class_register(&universe_class);
|
|
if (err) {
|
|
pr_err("Cannot register universe class: %d\n", err);
|
|
goto out_chrdev;
|
|
}
|
|
my_dev = device_create(&universe_class, NULL, MKDEV(student_chrdev_major, 0), NULL, STUDENT_CHRDEV);
|
|
if (IS_ERR(my_dev)) {
|
|
err = PTR_ERR(my_dev);
|
|
pr_err("Cannot create device: %d\n", err);
|
|
goto out_class;
|
|
}
|
|
pr_notice("Loaded module %s\n", KBUILD_MODNAME);
|
|
return 0;
|
|
|
|
out_class:
|
|
class_unregister(&universe_class);
|
|
out_chrdev:
|
|
unregister_chrdev(student_chrdev_major, STUDENT_CHRDEV);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static void __exit student_chrdevexit(void) {
|
|
device_destroy(&universe_class, MKDEV(student_chrdev_major, 0));
|
|
class_unregister(&universe_class);
|
|
unregister_chrdev(student_chrdev_major, STUDENT_CHRDEV);
|
|
pr_notice("Unloaded module %s\n", KBUILD_MODNAME);
|
|
}
|
|
|
|
module_init(student_chrdevinit);
|
|
module_exit(student_chrdevexit);
|
|
MODULE_LICENSE("LGPL");
|