#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #define st_debug(format, ...) \ pr_debug("(file:%s,line:%d,pid:%d): " format, \ __FILE__, __LINE__, \ current->pid, ##__VA_ARGS__) #include #include #include #include #include #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");