/* * $Id: kern_devconfig.c,v 1.11 2007/12/16 23:16:09 nbrk Exp $ */ #include #include #include #include #include #include #include /* #define DEVCONFIG_DEBUG */ #ifdef DEVCONFIG_DEBUG #define DPRINTF(x...) do { printf(x); } while (0) #else #define DPRINTF(x...) { } #endif /* system root device */ extern struct device *rootdev; /* config stuff */ extern struct attachinfo config_attachinfo[]; extern struct driverinfo config_driverinfo[]; void devconfig_recurse(void) { /* * Recursive attach all device tree. * Steps involved: * - find root device in device list and attach it * - for every root's child do: * - attach this child * - for every child's child do: * - attach that child * - ...and so on. */ devconfig_attach_root(); } void devconfig_attach_root(void) { /* * Attach root and call devconfig_attach on itself (proceeding with its children). */ int retval; struct driver *drp; struct driverinfo *dip; DPRINTF("devconfig_attach_root: entering\n"); /* allocate memory for root device */ rootdev = kmalloc(sizeof(struct device)); if (rootdev == NULL) panic("failed to allocate space for rootdev\n"); /* NOTREACHED */ /* find root's driverinfo */ dip = devconfig_finddriverinfo("root"); drp = dip->di_driverp; /* allocate space for root's devdata */ rootdev->dv_devdata = kmalloc(drp->dr_ddsize); if(rootdev->dv_devdata == NULL) panic("failed to allocate space for rootdev devdata\n"); /* NOTREACHED */ /* increment number of this driver's instances */ dip->di_ninstances++; rootdev->dv_minor = dip->di_ninstances; rootdev->dv_name = dip->di_dname; /* link dv_parent into root itself */ rootdev->dv_parent = rootdev; printf("%s/%d at %s/%d loc 0x%x flags 0x%x: ", rootdev->dv_name, rootdev->dv_minor, rootdev->dv_parent->dv_name, rootdev->dv_parent->dv_minor, 0, 0); /* attach root; should never fail */ retval = drp->dr_attach(rootdev, 0, 0); if (retval != 0) panic("failed to attach rootdev\n"); /* NOTREACHED */ /* rootdev has been attached, so activate it */ rootdev->dv_active = 1; /* attach all other devices */ devconfig_attach_childs(rootdev); } int devconfig_attach_childs(struct device *pdevp) { /* * Attach device childs, if any. * Allocate space for struct device and its xxx_dd */ int retval; struct device *cdevp; struct driver *drp; struct driverinfo *dip; struct attachinfo *aip = config_attachinfo; DPRINTF("devconfig_attach_childs: entering for %s/%d\n", pdevp->dv_name, pdevp->dv_minor); /* look for potential children in config_attachinfo table */ while(aip->ai_cname != NULL) { if (strncmp(aip->ai_pname, pdevp->dv_name, DVNAMELEN - 1) == 0 && aip->ai_pminor == pdevp->dv_minor) { /* * It is our child. */ /* find child's driverinfo */ dip = devconfig_finddriverinfo(aip->ai_cname); /* allocate space for child's device */ cdevp = kmalloc(sizeof(struct device)); if (cdevp == NULL) panic("failed to allocate space for %s device\n", aip->ai_cname); /* NOTREACHED */ /* link with parent */ cdevp->dv_parent = pdevp; drp = dip->di_driverp; /* allocate space for device's devdata */ cdevp->dv_devdata = kmalloc(drp->dr_ddsize); if (cdevp->dv_devdata == NULL) panic("failed to allocate space for %s devdata\n", aip->ai_cname); /* NOTREACHED */ /* increment number of driver's instances */ dip->di_ninstances++; cdevp->dv_name = dip->di_dname; cdevp->dv_minor = dip->di_ninstances; printf("%s/%d at %s/%d loc 0x%x flags 0x%x: ", cdevp->dv_name, cdevp->dv_minor, pdevp->dv_name, pdevp->dv_minor, aip->ai_locator, aip->ai_flags); /* try to attach this device */ retval = drp->dr_attach(cdevp, aip->ai_locator, aip->ai_flags); if (retval != 0) { /* * Attachment failed. */ /* XXX what should we do? */ printf("disabling %s/%d\n", cdevp->dv_name, cdevp->dv_minor); /* TODO kfree devdata and device */ /* next aip */ aip++; continue; /* NOTREACHED */ } /* * Attachment completed. */ /* activate device */ cdevp->dv_active = 1; /* if device has interrupt handler, establish it */ if (drp->dr_interrupt != NULL && aip->ai_intrno != -1) { DPRINTF("devconfig_attach_childs: establishing interrupt %d for %s/%d\n", aip->ai_intrno, cdevp->dv_name, cdevp->dv_minor); intr_establish(aip->ai_intrno, cdevp, drp->dr_interrupt); } /* recursive attach this child's children */ devconfig_attach_childs(cdevp); } /* next attachinfo in table */ aip++; } } struct driverinfo *devconfig_finddriverinfo(const char *dname) { /* * Return associated with dname driver (from config_driverinfo[]) * Will cause SYSTEM PANIC if can't find associated driver. */ struct driverinfo *dip = config_driverinfo; while(dip->di_dname != NULL) { if (strncmp(dname, dip->di_dname, DVNAMELEN - 1) == 0) return(dip); dip++; } panic("failed to find driverinfo entry for %s\n", dname); /* NOTREACHED */ return(NULL); }