2024-07-16 15:50:57 +02:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
|
|
|
#include <linux/printk.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/cdev.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
|
|
|
|
#define VOGON_CHRDEV "vogon"
|
|
|
|
|
|
|
|
static char *poem[] = {
|
|
|
|
"Oh freddled gruntbuggly,",
|
|
|
|
"Thy micturations are to me, (with big yawning)",
|
|
|
|
"As plurdled gabbleblotchits, in midsummer morning",
|
|
|
|
"On a lurgid bee,",
|
|
|
|
"That mordiously hath blurted out,",
|
|
|
|
"Its earted jurtles, grumbling",
|
|
|
|
"Into a rancid festering confectious organ squealer. [drowned out by moaning and screaming]",
|
|
|
|
"Now the jurpling slayjid agrocrustles,",
|
|
|
|
"Are slurping hagrilly up the axlegrurts,",
|
|
|
|
"And living glupules frart and stipulate,",
|
|
|
|
"Like jowling meated liverslime,",
|
|
|
|
"Groop, I implore thee, my foonting turlingdromes,",
|
|
|
|
"And hooptiously drangle me,",
|
|
|
|
"With crinkly bindlewurdles,mashurbitries.",
|
|
|
|
"Or else I shall rend thee in the gobberwarts with my blurglecruncheon,",
|
|
|
|
"See if I don't!",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
static int poem_len, poem_line;
|
|
|
|
static spinlock_t poem_lock;
|
|
|
|
static int vogon_chrdev_major = 0;
|
|
|
|
static struct device *my_dev = NULL;
|
|
|
|
|
|
|
|
void vogon_poem_inc(void) {
|
|
|
|
spin_lock(&poem_lock);
|
|
|
|
if (poem[poem_line] == NULL) {
|
|
|
|
poem_line = 0;
|
|
|
|
}else {
|
|
|
|
poem_line++;
|
|
|
|
}
|
|
|
|
spin_unlock(&poem_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vogon_peom(struct seq_file *p, void *v) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// In the dusk of an Operating System, the solution lies beyond its bounds.
|
2024-07-31 15:56:45 +02:00
|
|
|
spin_lock_bh(&poem_lock);
|
2024-07-16 15:50:57 +02:00
|
|
|
i = poem_line;
|
2024-07-31 15:56:45 +02:00
|
|
|
spin_unlock_bh(&poem_lock);
|
2024-07-16 15:50:57 +02:00
|
|
|
seq_printf(p, "%s\n", poem[i]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vogon_open(struct inode *inode, struct file *file) {
|
|
|
|
return single_open(file, vogon_peom, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct file_operations vogon_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = vogon_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = single_release
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct class universe_class = {
|
|
|
|
.name = VOGON_CHRDEV,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init vogon_chrdevinit(void) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
for (int i = 0; poem[i]; i++) {
|
|
|
|
poem_len++;
|
|
|
|
}
|
|
|
|
spin_lock_init(&poem_lock);
|
|
|
|
|
|
|
|
vogon_chrdev_major = err = register_chrdev(0, VOGON_CHRDEV, &vogon_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(vogon_chrdev_major, 0), NULL, VOGON_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(vogon_chrdev_major, VOGON_CHRDEV);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit vogon_chrdevexit(void) {
|
|
|
|
device_destroy(&universe_class, MKDEV(vogon_chrdev_major, 0));
|
|
|
|
class_unregister(&universe_class);
|
|
|
|
unregister_chrdev(vogon_chrdev_major, VOGON_CHRDEV);
|
|
|
|
pr_notice("Unloaded module %s\n", KBUILD_MODNAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(vogon_chrdevinit);
|
|
|
|
module_exit(vogon_chrdevexit);
|
|
|
|
MODULE_LICENSE("LGPL");
|