[BACK]Return to kern_devctl.c CVS log [TXT][DIR] Up to [local] / funnyos / kern

File: [local] / funnyos / kern / kern_devctl.c (download)

Revision 1.1, Sun Dec 16 23:03:00 2007 UTC (16 years, 4 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD

introducing devctl, a mechanism to talk to device drivers from other drivers or userland.
interested drivers should implement callback function (xxx_devctl) and register it with devctl_register();
once registered, all other FunnyOS subsystems should be able to pass in/out some control commands
and data to/from device driver in subject.

for example, to toggle onboard LED on/off (see next commits) one will use: devctl("gpioled", 0, DCGPIOLED_TOGGLE, NULL);
DCGPIOLED is a 'ctlcmd' and NULL is a 'ctldata'. ctlcmd is a command recognized by that driver (gpioled);

this is similar to ioctl(2) in UNIX.

/*
 * $id$
 */
#include <sys/types.h>
#include <sys/device.h>
#include <sys/devctl.h>
#include <sys/mem.h>

#include <libkern/printf.h>
#include <libkern/string.h>

/*
 * devctl, Device control mechanism.
 * devctl is used when driver or task want to talk to a specified attached device,
 * Device driver (if it wishes to) implements devctl stub and registers it with devctl_register();
 * Device driver also defines (for himself) a set of meanings of 32bit 'ctlcmd' field which will be
 *  passed to his devctl stub among with 'ctldata' (which is a void *).
 * In summary, picture look like this:
 *   - device driver (foodev) implements and registers its devctl handler (foo_devctl()).
 *   - another driver (bardev) or task wants to talk to foodev/0 and calls: devctl("foodev", 0, READ_DATA, buffer);
 *   - program flow gets in foo_devctl(struct device *self, ctlcmd, ctldata);
 *   - foo driver deciphers ctlcmd ("READ_DATA"), performs requested action and passes results back to 'bar' in ctldata ("buffer").
 */

struct devctl_vector {
	struct device 	*dcv_devp;
	char 			*dcv_dvname;
	uint8_t 		dcv_dvminor;
	int 			(*dcv_ctlfunc)(struct device *self, uint32_t ctlcmd, void *ctldata);

	struct devctl_vector *dcv_next;
};

/* system devctl table */
struct devctl_vector *devctltable = NULL;


void
devctl_register(struct device *self, int (*ctlfunc)(struct device *self, uint32_t ctlcmd, void *ctldata))
{
	struct devctl_vector *dcvp, *prevdcvp;

	/*
	 * Register given function as devctl stub for given device.
	 */

	dcvp = devctltable;

	if (dcvp == NULL) {
		/*
		 * First entry.
		 */
		dcvp = kmalloc(sizeof(struct devctl_vector));
		if (dcvp == NULL) {
			panic("devctl_register: failed to allocate memory\n");
			/* NOTREACHED */
		}

		/* update list head */
		devctltable = dcvp;
	} else {
		/*
		 * Append to list.
		 */
		while(dcvp->dcv_next != NULL)
			dcvp = dcvp->dcv_next;

		/* found last entry, allocate space for next (new) */
		prevdcvp = dcvp;
		dcvp->dcv_next = kmalloc(sizeof(struct devctl_vector));
		if (dcvp->dcv_next == NULL)
			panic("devctl_register: failed to allocate memory\n");

		/* ok, jump forward */
		dcvp = dcvp->dcv_next;

		prevdcvp->dcv_next = dcvp;
	}

	/* write entry; set dcv_next to NULL indicating end of list */
	dcvp->dcv_devp = self;
	dcvp->dcv_dvname = self->dv_name;
	dcvp->dcv_dvminor = self->dv_minor;
	dcvp->dcv_ctlfunc = ctlfunc;
	dcvp->dcv_next = NULL;
}


int
devctl(const char *dv_name, uint8_t dv_minor, uint32_t ctlcmd, void *ctldata)
{
	struct devctl_vector *dcvp;

	dcvp = devctltable;

	if (dcvp == NULL) {
		/* devctl table is empty */
		/* XXX panic here? */
		panic("devctl: can't find devctl for %s/%d: devctltable is not configured yet\n", dv_name, dv_minor);
		/* NOTREACHED */
	}

	/* look through the list */
	while(dcvp != NULL) {

		if (strncmp(dcvp->dcv_dvname, dv_name, DVNAMELEN - 1) == 0 && dcvp->dcv_dvminor == dv_minor)
			/* got it */
			return(dcvp->dcv_ctlfunc(dcvp->dcv_devp, ctlcmd, ctldata));

		dcvp = dcvp->dcv_next;
	}

	/* requested devctl not found */
	panic("devctl: can't find devctl for %s/%d: devctltable record not found\n", dv_name, dv_minor);
}