Kernel module: return a number from a device

From: Rocky Hotas <rockyhotas_at_tilde.team>
Date: Sat, 06 Apr 2024 20:27:12 UTC
Hello!
I'm trying to write a simple kernel module, using as a model the example
in

 <https://docs.freebsd.org/en/books/arch-handbook/driverbasics/>

I am a newbie. My module should be simpler than the one in the link: it
should just create a read-only /dev/rolld file; each time it is read
by the user (for example through `cat'), the file should provide a
random number mod d_size. So, the "output" should always be 1 character.

I modified the echo kernel module presented in the link. My module
can successfully be loaded into the kernel and the device is created,
but if I run as a user `cat /dev/rolld':

$ cat /dev/rolld 
Opened device "rolld" successfully.

and it hangs, giving no more output and without generating an error.

May be this due to the fact that uiomove receives a pointer &random_out,
which is a pointer to a uint32_t instead of for example a char? (And if
this is the issue, how to convert a uint32_t to char inside the kernel?)

Or is there some other error that I made?

I paste my code below.

Bye!

Rocky



#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/libkern.h>

static d_open_t      rolld_open;
static d_close_t     rolld_close;
static d_read_t      rolld_read;

static struct cdevsw rolld_cdevsw = {
	.d_version = D_VERSION,
	.d_open = rolld_open,
	.d_close = rolld_close,
	.d_read = rolld_read,
	.d_name = "rolld",
};

/* vars */
static struct cdev *rolld_dev;
static uint32_t d_size = 6;

static int
rolld_loader(struct module *m __unused, int what, void *arg __unused)
{
	int error = 0;

	switch (what) {
	case MOD_LOAD:                /* kldload */
		error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
		    &rolld_dev,
		    &rolld_cdevsw,
		    0,
		    UID_ROOT,
		    GID_WHEEL,
		    0444,
		    "rolld");
		if (error != 0)
			break;

		printf("Roll device loaded.\n");
		break;
	case MOD_UNLOAD:
		destroy_dev(rolld_dev);
		printf("Roll device unloaded.\n");
		break;
	default:
		error = EOPNOTSUPP;
		break;
	}
	return (error);
}

static int
rolld_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
    struct thread *td __unused)
{
	int error = 0;

	uprintf("Opened device \"rolld\" successfully.\n");
	return (error);
}

static int
rolld_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,
    struct thread *td __unused)
{
	uprintf("Closing device \"rolld\".\n");
	return (0);
}

static int
rolld_read(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)
{
	uint32_t random_out;
	uint32_t random_item;
	int error;

	random_item = arc4random();
	random_out = random_item % d_size;

	if ((error = uiomove(&random_out, 1, uio)) != 0)
		uprintf("uiomove failed!\n");

	return (error);
}

DEV_MODULE(rolld, rolld_loader, NULL);