prom.c
字号:
if (of_node_get(next)) break; if (prev) of_node_put(prev); read_unlock(&devtree_lock); return next;}EXPORT_SYMBOL(of_get_next_child);/** * of_node_get - Increment refcount of a node * @node: Node to inc refcount, NULL is supported to * simplify writing of callers * * Returns node. */struct device_node *of_node_get(struct device_node *node){ if (node) kref_get(&node->kref); return node;}EXPORT_SYMBOL(of_node_get);static inline struct device_node * kref_to_device_node(struct kref *kref){ return container_of(kref, struct device_node, kref);}/** * of_node_release - release a dynamically allocated node * @kref: kref element of the node to be released * * In of_node_put() this function is passed to kref_put() * as the destructor. */static void of_node_release(struct kref *kref){ struct device_node *node = kref_to_device_node(kref); struct property *prop = node->properties; if (!OF_IS_DYNAMIC(node)) return; while (prop) { struct property *next = prop->next; kfree(prop->name); kfree(prop->value); kfree(prop); prop = next; } kfree(node->intrs); kfree(node->addrs); kfree(node->full_name); kfree(node);}/** * of_node_put - Decrement refcount of a node * @node: Node to dec refcount, NULL is supported to * simplify writing of callers * */void of_node_put(struct device_node *node){ if (node) kref_put(&node->kref, of_node_release);}EXPORT_SYMBOL(of_node_put);/** * derive_parent - basically like dirname(1) * @path: the full_name of a node to be added to the tree * * Returns the node which should be the parent of the node * described by path. E.g., for path = "/foo/bar", returns * the node with full_name = "/foo". */static struct device_node *derive_parent(const char *path){ struct device_node *parent = NULL; char *parent_path = "/"; size_t parent_path_len = strrchr(path, '/') - path + 1; /* reject if path is "/" */ if (!strcmp(path, "/")) return NULL; if (strrchr(path, '/') != path) { parent_path = kmalloc(parent_path_len, GFP_KERNEL); if (!parent_path) return NULL; strlcpy(parent_path, path, parent_path_len); } parent = of_find_node_by_path(parent_path); if (strcmp(parent_path, "/")) kfree(parent_path); return parent;}/* * Routines for "runtime" addition and removal of device tree nodes. */#ifdef CONFIG_PROC_DEVICETREE/* * Add a node to /proc/device-tree. */static void add_node_proc_entries(struct device_node *np){ struct proc_dir_entry *ent; ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde); if (ent) proc_device_tree_add_node(np, ent);}static void remove_node_proc_entries(struct device_node *np){ struct property *pp = np->properties; struct device_node *parent = np->parent; while (pp) { remove_proc_entry(pp->name, np->pde); pp = pp->next; } /* Assuming that symlinks have the same parent directory as * np->pde. */ if (np->name_link) remove_proc_entry(np->name_link->name, parent->pde); if (np->addr_link) remove_proc_entry(np->addr_link->name, parent->pde); if (np->pde) remove_proc_entry(np->pde->name, parent->pde);}#else /* !CONFIG_PROC_DEVICETREE */static void add_node_proc_entries(struct device_node *np){ return;}static void remove_node_proc_entries(struct device_node *np){ return;}#endif /* CONFIG_PROC_DEVICETREE *//* * Fix up n_intrs and intrs fields in a new device node * */static int of_finish_dynamic_node_interrupts(struct device_node *node){ int intrcells, intlen, i; unsigned *irq, *ints, virq; struct device_node *ic; ints = (unsigned int *)get_property(node, "interrupts", &intlen); intrcells = prom_n_intr_cells(node); intlen /= intrcells * sizeof(unsigned int); node->n_intrs = intlen; node->intrs = kmalloc(sizeof(struct interrupt_info) * intlen, GFP_KERNEL); if (!node->intrs) return -ENOMEM; for (i = 0; i < intlen; ++i) { int n, j; node->intrs[i].line = 0; node->intrs[i].sense = 1; n = map_interrupt(&irq, &ic, node, ints, intrcells); if (n <= 0) continue; virq = virt_irq_create_mapping(irq[0]); if (virq == NO_IRQ) { printk(KERN_CRIT "Could not allocate interrupt " "number for %s\n", node->full_name); return -ENOMEM; } node->intrs[i].line = irq_offset_up(virq); if (n > 1) node->intrs[i].sense = irq[1]; if (n > 2) { printk(KERN_DEBUG "hmmm, got %d intr cells for %s:", n, node->full_name); for (j = 0; j < n; ++j) printk(" %d", irq[j]); printk("\n"); } ints += intrcells; } return 0;}/* * Fix up the uninitialized fields in a new device node: * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields * * A lot of boot-time code is duplicated here, because functions such * as finish_node_interrupts, interpret_pci_props, etc. cannot use the * slab allocator. * * This should probably be split up into smaller chunks. */static int of_finish_dynamic_node(struct device_node *node){ struct device_node *parent = of_get_parent(node); u32 *regs; int err = 0; phandle *ibm_phandle; node->name = get_property(node, "name", NULL); node->type = get_property(node, "device_type", NULL); if (!parent) { err = -ENODEV; goto out; } /* We don't support that function on PowerMac, at least * not yet */ if (systemcfg->platform == PLATFORM_POWERMAC) return -ENODEV; /* fix up new node's linux_phandle field */ if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL))) node->linux_phandle = *ibm_phandle; /* do the work of interpret_pci_props */ if (parent->type && !strcmp(parent->type, "pci")) { struct address_range *adr; struct pci_reg_property *pci_addrs; int i, l; pci_addrs = (struct pci_reg_property *) get_property(node, "assigned-addresses", &l); if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { i = 0; adr = kmalloc(sizeof(struct address_range) * (l / sizeof(struct pci_reg_property)), GFP_KERNEL); if (!adr) { err = -ENOMEM; goto out; } while ((l -= sizeof(struct pci_reg_property)) >= 0) { adr[i].space = pci_addrs[i].addr.a_hi; adr[i].address = pci_addrs[i].addr.a_lo; adr[i].size = pci_addrs[i].size_lo; ++i; } node->addrs = adr; node->n_addrs = i; } } /* now do the work of finish_node_interrupts */ if (get_property(node, "interrupts", NULL)) { err = of_finish_dynamic_node_interrupts(node); if (err) goto out; } /* now do the rough equivalent of update_dn_pci_info, this * probably is not correct for phb's, but should work for * IOAs and slots. */ node->phb = parent->phb; regs = (u32 *)get_property(node, "reg", NULL); if (regs) { node->busno = (regs[0] >> 16) & 0xff; node->devfn = (regs[0] >> 8) & 0xff; }out: of_node_put(parent); return err;}/* * Given a path and a property list, construct an OF device node, add * it to the device tree and global list, and place it in * /proc/device-tree. This function may sleep. */int of_add_node(const char *path, struct property *proplist){ struct device_node *np; int err = 0; np = kmalloc(sizeof(struct device_node), GFP_KERNEL); if (!np) return -ENOMEM; memset(np, 0, sizeof(*np)); np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL); if (!np->full_name) { kfree(np); return -ENOMEM; } strcpy(np->full_name, path); np->properties = proplist; OF_MARK_DYNAMIC(np); kref_init(&np->kref); of_node_get(np); np->parent = derive_parent(path); if (!np->parent) { kfree(np); return -EINVAL; /* could also be ENOMEM, though */ } if (0 != (err = of_finish_dynamic_node(np))) { kfree(np); return err; } write_lock(&devtree_lock); np->sibling = np->parent->child; np->allnext = allnodes; np->parent->child = np; allnodes = np; write_unlock(&devtree_lock); add_node_proc_entries(np); of_node_put(np->parent); of_node_put(np); return 0;}/* * Prepare an OF node for removal from system */static void of_cleanup_node(struct device_node *np){ if (np->iommu_table && get_property(np, "ibm,dma-window", NULL)) iommu_free_table(np);}/* * "Unplug" a node from the device tree. The caller must hold * a reference to the node. The memory associated with the node * is not freed until its refcount goes to zero. */int of_remove_node(struct device_node *np){ struct device_node *parent, *child; parent = of_get_parent(np); if (!parent) return -EINVAL; if ((child = of_get_next_child(np, NULL))) { of_node_put(child); return -EBUSY; } of_cleanup_node(np); write_lock(&devtree_lock); remove_node_proc_entries(np); if (allnodes == np) allnodes = np->allnext; else { struct device_node *prev; for (prev = allnodes; prev->allnext != np; prev = prev->allnext) ; prev->allnext = np->allnext; } if (parent->child == np) parent->child = np->sibling; else { struct device_node *prevsib; for (prevsib = np->parent->child; prevsib->sibling != np; prevsib = prevsib->sibling) ; prevsib->sibling = np->sibling; } write_unlock(&devtree_lock); of_node_put(parent); of_node_put(np); /* Must decrement the refcount */ return 0;}/* * Find a property with a given name for a given node * and return the value. */unsigned char *get_property(struct device_node *np, const char *name, int *lenp){ struct property *pp; for (pp = np->properties; pp != 0; pp = pp->next) if (strcmp(pp->name, name) == 0) { if (lenp != 0) *lenp = pp->length; return pp->value; } return NULL;}/* * Add a property to a node */voidprom_add_property(struct device_node* np, struct property* prop){ struct property **next = &np->properties; prop->next = NULL; while (*next) next = &(*next)->next; *next = prop;}#if 0voidprint_properties(struct device_node *np){ struct property *pp; char *cp; int i, n; for (pp = np->properties; pp != 0; pp = pp->next) { printk(KERN_INFO "%s", pp->name); for (i = strlen(pp->name); i < 16; ++i) printk(" "); cp = (char *) pp->value; for (i = pp->length; i > 0; --i, ++cp) if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) || (i == 1 && *cp != 0)) break; if (i == 0 && pp->length > 1) { /* looks like a string */ printk(" %s\n", (char *) pp->value); } else { /* dump it in hex */ n = pp->length; if (n > 64) n = 64; if (pp->length % 4 == 0) { unsigned int *p = (unsigned int *) pp->value; n /= 4; for (i = 0; i < n; ++i) { if (i != 0 && (i % 4) == 0) printk("\n "); printk(" %08x", *p++); } } else { unsigned char *bp = pp->value; for (i = 0; i < n; ++i) { if (i != 0 && (i % 16) == 0) printk("\n "); printk(" %02x", *bp++); } } printk("\n"); if (pp->length > 64) printk(" ... (length = %d)\n", pp->length); } }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -