#include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("fef "); MODULE_DESCRIPTION("gay keymash character device driver"); MODULE_VERSION("0.1.1"); #define keymash_debug(msg, ...) printk(KERN_DEBUG "keymash: " msg, ##__VA_ARGS__) #define keymash_info(msg, ...) printk(KERN_INFO "keymash: " msg, ##__VA_ARGS__) #define keymash_warn(msg, ...) printk(KERN_WARN "keymash: " msg, ##__VA_ARGS__) #define keymash_err(msg, ...) printk(KERN_ERR "keymash: " msg, ##__VA_ARGS__) static dev_t keymash_dev = MKDEV(0, 0); static struct cdev keymash_cdev; static struct class *keymash_class; static struct device *keymash_device; #define KEYMASH_MINOR_COUNT 1 #define KEYMASH_DEVICE_NAME "keymash" #define KEYMASH_CLASS_NAME "kmsh" static const char keymash_chrs[] = "asdfghjkl;"; #define KEYMASH_CHRS_LEN (ARRAY_SIZE(keymash_chrs) - 1) /* minus NUL */ static void keymash_gay(void *buf, size_t len) { unsigned char *cbuf = buf; get_random_bytes(buf, len); while (len--) { unsigned char rnd = *cbuf; *cbuf++ = keymash_chrs[rnd % KEYMASH_CHRS_LEN]; } } static ssize_t keymash_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset) { ssize_t ret; void *gay = kmalloc(size, GFP_KERNEL); if (gay) { keymash_gay(gay, size); if (copy_to_user(user_buffer, gay, size)) ret = -EFAULT; else ret = size; kfree(gay); } else { ret = -ENOMEM; } return ret; } static const struct file_operations keymash_fops = { .owner = THIS_MODULE, .read = keymash_read, }; #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0) static char *keymash_devnode(struct device *dev, umode_t *mode) #else static char *keymash_devnode(const struct device *dev, umode_t *mode) #endif { if (mode) *mode = 0444; return NULL; } __init static int keymash_init(void) { int err = -1; err = alloc_chrdev_region(&keymash_dev, 0, KEYMASH_MINOR_COUNT, KEYMASH_DEVICE_NAME); if (err < 0) { keymash_err("alloc_chrdev_region() returned %d\n", err); goto err_alloc_chrdev_region; } keymash_debug("using device major number %d\n", MAJOR(keymash_dev)); cdev_init(&keymash_cdev, &keymash_fops); err = cdev_add(&keymash_cdev, keymash_dev, 1); if (err < 0) { keymash_err("cdev_init() returned %d\n", err); goto err_cdev_add; } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) keymash_class = class_create(THIS_MODULE, KEYMASH_CLASS_NAME); #else keymash_class = class_create(KEYMASH_CLASS_NAME); #endif if (IS_ERR(keymash_class)) { err = PTR_ERR(keymash_class); keymash_err("class_create() returned %d\n", err); goto err_class_create; } keymash_class->devnode = keymash_devnode; keymash_device = device_create(keymash_class, NULL, keymash_dev, NULL, KEYMASH_DEVICE_NAME); if (IS_ERR(keymash_device)) { err = PTR_ERR(keymash_device); keymash_err("device_create() returned %d\n", err); goto err_device_create; } return 0; err_device_create: class_unregister(keymash_class); class_destroy(keymash_class); err_class_create: cdev_del(&keymash_cdev); err_cdev_add: unregister_chrdev_region(keymash_dev, KEYMASH_MINOR_COUNT); err_alloc_chrdev_region: return err; } module_init(keymash_init); __exit static void keymash_exit(void) { device_destroy(keymash_class, keymash_dev); class_unregister(keymash_class); class_destroy(keymash_class); cdev_del(&keymash_cdev); unregister_chrdev_region(keymash_dev, KEYMASH_MINOR_COUNT); } module_exit(keymash_exit);