randpup.git

kernel.c

espurr
/* randpup - ngram text generator kernel module
 * Copyright (C) 2025 ArcNyxx
 * see LICENCE.MIT file for licensing information */

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>

#include "act.h"

MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("ArcNyxx");
MODULE_DESCRIPTION("puppygirl nonsense generator");

static int
pup_device_open(struct inode *inode, struct file *fp)
{
	if ((fp->private_data = kmalloc(sizeof(pup_state_t),
			GFP_KERNEL)) == NULL)
		return -ENOMEM;
	((pup_state_t *)(fp->private_data))->last = -1;
	pup_reset(fp->private_data);
	return 0;
}

static int
pup_device_close(struct inode *inode, struct file *fp)
{
	kfree(fp->private_data);
	fp->private_data = NULL;
	return 0;
}

static ssize_t
pup_device_read(struct file *fp, char __user *buffer, size_t length,
		loff_t *offset)
{
	return pup_write(fp->private_data, buffer, length);
}

static struct file_operations fops = {
	.open = pup_device_open,
	.release = pup_device_close,
	.read = pup_device_read
};
static dev_t pup_dev;
static struct cdev pup_cdev;
static struct class *pup_class;

static int
pup_module_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
	return add_uevent_var(env, "DEVMODE=%#o", 0666);
}

static int __init
pup_module_init(void)
{
	if (alloc_chrdev_region(&pup_dev, 0, 1, "randpup") != 0)
		return 1;

	cdev_init(&pup_cdev, &fops);
	pup_cdev.owner = THIS_MODULE;
	if (cdev_add(&pup_cdev, pup_dev, 1) < 0) {
		unregister_chrdev_region(pup_dev, 1);
		return 1;
	}

	if (IS_ERR(pup_class = class_create("randpup"))) {
		unregister_chrdev_region(pup_dev, 1);
		cdev_del(&pup_cdev);
		return 1;
	}

	pup_class->dev_uevent = pup_module_uevent;
	if (IS_ERR(device_create(pup_class, NULL, pup_dev, NULL, "randpup"))) {
		class_destroy(pup_class);
		cdev_del(&pup_cdev);
		unregister_chrdev_region(pup_dev, 1);
		return 1;
	}

	return 0;
}

static void __exit
pup_module_exit(void)
{
	device_destroy(pup_class, pup_dev);
	class_destroy(pup_class);
	cdev_del(&pup_cdev);
	unregister_chrdev_region(pup_dev, 1);
}

module_init(pup_module_init);
module_exit(pup_module_exit);