| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * proc_devtree.c - handles /proc/device-tree | 
 | 3 |  * | 
 | 4 |  * Copyright 1997 Paul Mackerras | 
 | 5 |  */ | 
 | 6 | #include <linux/errno.h> | 
 | 7 | #include <linux/time.h> | 
 | 8 | #include <linux/proc_fs.h> | 
 | 9 | #include <linux/stat.h> | 
 | 10 | #include <linux/string.h> | 
 | 11 | #include <asm/prom.h> | 
 | 12 | #include <asm/uaccess.h> | 
 | 13 |  | 
 | 14 | #ifndef HAVE_ARCH_DEVTREE_FIXUPS | 
 | 15 | static inline void set_node_proc_entry(struct device_node *np, struct proc_dir_entry *de) | 
 | 16 | { | 
 | 17 | } | 
 | 18 |  | 
 | 19 | static void inline set_node_name_link(struct device_node *np, struct proc_dir_entry *de) | 
 | 20 | { | 
 | 21 | } | 
 | 22 |  | 
 | 23 | static void inline set_node_addr_link(struct device_node *np, struct proc_dir_entry *de) | 
 | 24 | { | 
 | 25 | } | 
 | 26 | #endif | 
 | 27 |  | 
 | 28 | static struct proc_dir_entry *proc_device_tree; | 
 | 29 |  | 
 | 30 | /* | 
 | 31 |  * Supply data on a read from /proc/device-tree/node/property. | 
 | 32 |  */ | 
 | 33 | static int property_read_proc(char *page, char **start, off_t off, | 
 | 34 | 			      int count, int *eof, void *data) | 
 | 35 | { | 
 | 36 | 	struct property *pp = data; | 
 | 37 | 	int n; | 
 | 38 |  | 
 | 39 | 	if (off >= pp->length) { | 
 | 40 | 		*eof = 1; | 
 | 41 | 		return 0; | 
 | 42 | 	} | 
 | 43 | 	n = pp->length - off; | 
 | 44 | 	if (n > count) | 
 | 45 | 		n = count; | 
 | 46 | 	else | 
 | 47 | 		*eof = 1; | 
 | 48 | 	memcpy(page, pp->value + off, n); | 
 | 49 | 	*start = page; | 
 | 50 | 	return n; | 
 | 51 | } | 
 | 52 |  | 
 | 53 | /* | 
 | 54 |  * For a node with a name like "gc@10", we make symlinks called "gc" | 
 | 55 |  * and "@10" to it. | 
 | 56 |  */ | 
 | 57 |  | 
 | 58 | /* | 
 | 59 |  * Process a node, adding entries for its children and its properties. | 
 | 60 |  */ | 
 | 61 | void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *de) | 
 | 62 | { | 
 | 63 | 	struct property *pp; | 
 | 64 | 	struct proc_dir_entry *ent; | 
 | 65 | 	struct device_node *child, *sib; | 
 | 66 | 	const char *p, *at; | 
 | 67 | 	int l; | 
 | 68 | 	struct proc_dir_entry *list, **lastp, *al; | 
 | 69 |  | 
 | 70 | 	set_node_proc_entry(np, de); | 
 | 71 | 	lastp = &list; | 
 | 72 | 	for (pp = np->properties; pp != 0; pp = pp->next) { | 
 | 73 | 		/* | 
 | 74 | 		 * Unfortunately proc_register puts each new entry | 
 | 75 | 		 * at the beginning of the list.  So we rearrange them. | 
 | 76 | 		 */ | 
 | 77 | 		ent = create_proc_read_entry(pp->name, strncmp(pp->name, "security-", 9) ? | 
 | 78 | 					     S_IRUGO : S_IRUSR, de, property_read_proc, pp); | 
 | 79 | 		if (ent == 0) | 
 | 80 | 			break; | 
 | 81 | 		if (!strncmp(pp->name, "security-", 9)) | 
 | 82 | 		     ent->size = 0; /* don't leak number of password chars */ | 
 | 83 | 		else | 
 | 84 | 		     ent->size = pp->length; | 
 | 85 | 		*lastp = ent; | 
 | 86 | 		lastp = &ent->next; | 
 | 87 | 	} | 
 | 88 | 	child = NULL; | 
 | 89 | 	while ((child = of_get_next_child(np, child))) { | 
 | 90 | 		p = strrchr(child->full_name, '/'); | 
 | 91 | 		if (!p) | 
 | 92 | 			p = child->full_name; | 
 | 93 | 		else | 
 | 94 | 			++p; | 
 | 95 | 		/* chop off '@0' if the name ends with that */ | 
 | 96 | 		l = strlen(p); | 
 | 97 | 		if (l > 2 && p[l-2] == '@' && p[l-1] == '0') | 
 | 98 | 			l -= 2; | 
 | 99 | 		ent = proc_mkdir(p, de); | 
 | 100 | 		if (ent == 0) | 
 | 101 | 			break; | 
 | 102 | 		*lastp = ent; | 
 | 103 | 		lastp = &ent->next; | 
 | 104 | 		proc_device_tree_add_node(child, ent); | 
 | 105 |  | 
 | 106 | 		/* | 
 | 107 | 		 * If we left the address part on the name, consider | 
 | 108 | 		 * adding symlinks from the name and address parts. | 
 | 109 | 		 */ | 
 | 110 | 		if (p[l] != 0 || (at = strchr(p, '@')) == 0) | 
 | 111 | 			continue; | 
 | 112 |  | 
 | 113 | 		/* | 
 | 114 | 		 * If this is the first node with a given name property, | 
 | 115 | 		 * add a symlink with the name property as its name. | 
 | 116 | 		 */ | 
 | 117 | 		sib = NULL; | 
 | 118 | 		while ((sib = of_get_next_child(np, sib)) && sib != child) | 
 | 119 | 			if (sib->name && strcmp(sib->name, child->name) == 0) | 
 | 120 | 				break; | 
 | 121 | 		if (sib == child && strncmp(p, child->name, l) != 0) { | 
 | 122 | 			al = proc_symlink(child->name, de, ent->name); | 
 | 123 | 			if (al == 0) { | 
 | 124 | 				of_node_put(sib); | 
 | 125 | 				break; | 
 | 126 | 			} | 
 | 127 | 			set_node_name_link(child, al); | 
 | 128 | 			*lastp = al; | 
 | 129 | 			lastp = &al->next; | 
 | 130 | 		} | 
 | 131 | 		of_node_put(sib); | 
 | 132 | 		/* | 
 | 133 | 		 * Add another directory with the @address part as its name. | 
 | 134 | 		 */ | 
 | 135 | 		al = proc_symlink(at, de, ent->name); | 
 | 136 | 		if (al == 0) | 
 | 137 | 			break; | 
 | 138 | 		set_node_addr_link(child, al); | 
 | 139 | 		*lastp = al; | 
 | 140 | 		lastp = &al->next; | 
 | 141 | 	} | 
 | 142 | 	of_node_put(child); | 
 | 143 | 	*lastp = NULL; | 
 | 144 | 	de->subdir = list; | 
 | 145 | } | 
 | 146 |  | 
 | 147 | /* | 
 | 148 |  * Called on initialization to set up the /proc/device-tree subtree | 
 | 149 |  */ | 
 | 150 | void proc_device_tree_init(void) | 
 | 151 | { | 
 | 152 | 	struct device_node *root; | 
 | 153 | 	if ( !have_of ) | 
 | 154 | 		return; | 
 | 155 | 	proc_device_tree = proc_mkdir("device-tree", NULL); | 
 | 156 | 	if (proc_device_tree == 0) | 
 | 157 | 		return; | 
 | 158 | 	root = of_find_node_by_path("/"); | 
 | 159 | 	if (root == 0) { | 
 | 160 | 		printk(KERN_ERR "/proc/device-tree: can't find root\n"); | 
 | 161 | 		return; | 
 | 162 | 	} | 
 | 163 | 	proc_device_tree_add_node(root, proc_device_tree); | 
 | 164 | 	of_node_put(root); | 
 | 165 | } |