Import 2.3.26pre2
[davej-history.git] / fs / proc / proc_devtree.c
blobc06a802ce0afffab58456f5f0a0ea82efb2b26cb
1 /*
2 * proc_devtree.c - handles /proc/device-tree
4 * Copyright 1997 Paul Mackerras
5 */
6 #include <linux/errno.h>
7 #include <linux/sched.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>
14 static struct proc_dir_entry *proc_device_tree;
17 * Supply data on a read from /proc/device-tree/node/property.
19 static int property_read_proc(char *page, char **start, off_t off,
20 int count, int *eof, void *data)
22 struct property *pp = data;
23 int n;
25 if (off >= pp->length) {
26 *eof = 1;
27 return 0;
29 n = pp->length - off;
30 if (n > count)
31 n = count;
32 else
33 *eof = 1;
34 memcpy(page, pp->value + off, n);
35 *start = page;
36 return n;
40 * For a node with a name like "gc@10", we make symlinks called "gc"
41 * and "@10" to it.
44 static int devtree_readlink(struct dentry *, char *, int);
45 static struct dentry *devtree_follow_link(struct dentry *, struct dentry *, unsigned int);
47 struct inode_operations devtree_symlink_inode_operations = {
48 NULL, /* no file-operations */
49 NULL, /* create */
50 NULL, /* lookup */
51 NULL, /* link */
52 NULL, /* unlink */
53 NULL, /* symlink */
54 NULL, /* mkdir */
55 NULL, /* rmdir */
56 NULL, /* mknod */
57 NULL, /* rename */
58 devtree_readlink, /* readlink */
59 devtree_follow_link, /* follow_link */
60 NULL, /* get_block */
61 NULL, /* readpage */
62 NULL, /* writepage */
63 NULL, /* flushpage */
64 NULL, /* truncate */
65 NULL, /* permission */
66 NULL, /* smap */
67 NULL /* revalidate */
70 static struct dentry *devtree_follow_link(struct dentry *dentry,
71 struct dentry *base,
72 unsigned int follow)
74 struct inode *inode = dentry->d_inode;
75 struct proc_dir_entry * de;
76 char *link;
78 de = (struct proc_dir_entry *) inode->u.generic_ip;
79 link = (char *) de->data;
80 return lookup_dentry(link, base, follow);
83 static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
85 struct inode *inode = dentry->d_inode;
86 struct proc_dir_entry * de;
87 char *link;
88 int linklen;
90 de = (struct proc_dir_entry *) inode->u.generic_ip;
91 link = (char *) de->data;
92 linklen = strlen(link);
93 if (linklen > buflen)
94 linklen = buflen;
95 if (copy_to_user(buffer, link, linklen))
96 return -EFAULT;
97 return linklen;
101 * Process a node, adding entries for its children and its properties.
103 static void add_node(struct device_node *np, struct proc_dir_entry *de)
105 struct property *pp;
106 struct proc_dir_entry *ent;
107 struct device_node *child, *sib;
108 const char *p, *at;
109 int l;
110 struct proc_dir_entry *list, **lastp, *al;
112 lastp = &list;
113 for (pp = np->properties; pp != 0; pp = pp->next) {
115 * Unfortunately proc_register puts each new entry
116 * at the beginning of the list. So we rearrange them.
118 ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
119 if (ent == 0)
120 break;
121 memset(ent, 0, sizeof(struct proc_dir_entry));
122 ent->name = pp->name;
123 ent->namelen = strlen(pp->name);
124 ent->mode = S_IFREG | S_IRUGO;
125 ent->nlink = 1;
126 ent->data = pp;
127 ent->read_proc = property_read_proc;
128 ent->size = pp->length;
129 proc_register(de, ent);
130 *lastp = ent;
131 lastp = &ent->next;
133 for (child = np->child; child != 0; child = child->sibling) {
134 p = strrchr(child->full_name, '/');
135 if (p == 0)
136 p = child->full_name;
137 else
138 ++p;
139 /* chop off '@0' if the name ends with that */
140 l = strlen(p);
141 if (l > 2 && p[l-2] == '@' && p[l-1] == '0')
142 l -= 2;
143 ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
144 if (ent == 0)
145 break;
146 memset(ent, 0, sizeof(struct proc_dir_entry));
147 ent->name = p;
148 ent->namelen = l;
149 ent->mode = S_IFDIR | S_IRUGO | S_IXUGO;
150 ent->nlink = 2;
151 proc_register(de, ent);
152 *lastp = ent;
153 lastp = &ent->next;
154 add_node(child, ent);
157 * If we left the address part on the name, consider
158 * adding symlinks from the name and address parts.
160 if (p[l] != 0 || (at = strchr(p, '@')) == 0)
161 continue;
164 * If this is the first node with a given name property,
165 * add a symlink with the name property as its name.
167 for (sib = np->child; sib != child; sib = sib->sibling)
168 if (sib->name && strcmp(sib->name, child->name) == 0)
169 break;
170 if (sib == child && strncmp(p, child->name, l) != 0) {
171 al = kmalloc(sizeof(struct proc_dir_entry),
172 GFP_KERNEL);
173 if (al == 0)
174 break;
175 memset(al, 0, sizeof(struct proc_dir_entry));
176 al->name = child->name;
177 al->namelen = strlen(child->name);
178 al->mode = S_IFLNK | S_IRUGO | S_IXUGO;
179 al->nlink = 1;
180 al->data = (void *) ent->name;
181 al->ops = &devtree_symlink_inode_operations;
182 proc_register(de, al);
183 *lastp = al;
184 lastp = &al->next;
188 * Add another directory with the @address part as its name.
190 al = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
191 if (al == 0)
192 break;
193 memset(al, 0, sizeof(struct proc_dir_entry));
194 al->name = at;
195 al->namelen = strlen(at);
196 al->mode = S_IFLNK | S_IRUGO | S_IXUGO;
197 al->nlink = 1;
198 al->data = (void *) ent->name;
199 al->ops = &devtree_symlink_inode_operations;
200 proc_register(de, al);
201 *lastp = al;
202 lastp = &al->next;
204 *lastp = 0;
205 de->subdir = list;
209 * Called on initialization to set up the /proc/device-tree subtree
211 void proc_device_tree_init(void)
213 struct device_node *root;
214 if ( !have_of )
215 return;
216 proc_device_tree = create_proc_entry("device-tree", S_IFDIR, 0);
217 if (proc_device_tree == 0)
218 return;
219 root = find_path_device("/");
220 if (root == 0) {
221 printk(KERN_ERR "/proc/device-tree: can't find root\n");
222 return;
224 add_node(root, proc_device_tree);