File: [local] / funnyos / kern / kern_devctl.c (download)
Revision 1.1, Sun Dec 16 23:03:00 2007 UTC (16 years, 6 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);
}