/* * $id$ */ #include #include #include #include #include #include /* * 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); }