linuxdebug/drivers/student/student.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");