Linux 2.2.0
[davej-history.git] / fs / proc / proc_devtree.c
blobcd4aca324eca70f3c2fd50008264096e3f698b37
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, /* readpage */
61 NULL, /* writepage */
62 NULL, /* bmap */
63 NULL, /* truncate */
64 NULL, /* permission */
65 NULL /* smap */
68 static struct dentry *devtree_follow_link(struct dentry *dentry,
69 struct dentry *base,
70 unsigned int follow)
72 struct inode *inode = dentry->d_inode;
73 struct proc_dir_entry * de;
74 char *link;
76 de = (struct proc_dir_entry *) inode->u.generic_ip;
77 link = (char *) de->data;
78 return lookup_dentry(link, base, follow);
81 static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
83 struct inode *inode = dentry->d_inode;
84 struct proc_dir_entry * de;
85 char *link;
86 int linklen;
88 de = (struct proc_dir_entry *) inode->u.generic_ip;
89 link = (char *) de->data;
90 linklen = strlen(link);
91 if (linklen > buflen)
92 linklen = buflen;
93 if (copy_to_user(buffer, link, linklen))
94 return -EFAULT;
95 return linklen;
99 * Process a node, adding entries for its children and its properties.
101 static void add_node(struct device_node *np, struct proc_dir_entry *de)
103 struct property *pp;
104 struct proc_dir_entry *ent;
105 struct device_node *child, *sib;
106 const char *p, *at;
107 int l;
108 struct proc_dir_entry *list, **lastp, *al;
110 lastp = &list;
111 for (pp = np->properties; pp != 0; pp = pp->next) {
113 * Unfortunately proc_register puts each new entry
114 * at the beginning of the list. So we rearrange them.
116 ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
117 if (ent == 0)
118 break;
119 memset(ent, 0, sizeof(struct proc_dir_entry));
120 ent->name = pp->name;
121 ent->namelen = strlen(pp->name);
122 ent->mode = S_IFREG | S_IRUGO;
123 ent->nlink = 1;
124 ent->data = pp;
125 ent->read_proc = property_read_proc;
126 ent->size = pp->length;
127 proc_register(de, ent);
128 *lastp = ent;
129 lastp = &ent->next;
131 for (child = np->child; child != 0; child = child->sibling) {
132 p = strrchr(child->full_name, '/');
133 if (p == 0)
134 p = child->full_name;
135 else
136 ++p;
137 /* chop off '@0' if the name ends with that */
138 l = strlen(p);
139 if (l > 2 && p[l-2] == '@' && p[l-1] == '0')
140 l -= 2;
141 ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
142 if (ent == 0)
143 break;
144 memset(ent, 0, sizeof(struct proc_dir_entry));
145 ent->name = p;
146 ent->namelen = l;
147 ent->mode = S_IFDIR | S_IRUGO | S_IXUGO;
148 ent->nlink = 2;
149 proc_register(de, ent);
150 *lastp = ent;
151 lastp = &ent->next;
152 add_node(child, ent);
155 * If we left the address part on the name, consider
156 * adding symlinks from the name and address parts.
158 if (p[l] != 0 || (at = strchr(p, '@')) == 0)
159 continue;
162 * If this is the first node with a given name property,
163 * add a symlink with the name property as its name.
165 for (sib = np->child; sib != child; sib = sib->sibling)
166 if (strcmp(sib->name, child->name) == 0)
167 break;
168 if (sib == child && strncmp(p, child->name, l) != 0) {
169 al = kmalloc(sizeof(struct proc_dir_entry),
170 GFP_KERNEL);
171 if (al == 0)
172 break;
173 memset(al, 0, sizeof(struct proc_dir_entry));
174 al->name = child->name;
175 al->namelen = strlen(child->name);
176 al->mode = S_IFLNK | S_IRUGO | S_IXUGO;
177 al->nlink = 1;
178 al->data = (void *) ent->name;
179 al->ops = &devtree_symlink_inode_operations;
180 proc_register(de, al);
181 *lastp = al;
182 lastp = &al->next;
186 * Add another directory with the @address part as its name.
188 al = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
189 if (al == 0)
190 break;
191 memset(al, 0, sizeof(struct proc_dir_entry));
192 al->name = at;
193 al->namelen = strlen(at);
194 al->mode = S_IFLNK | S_IRUGO | S_IXUGO;
195 al->nlink = 1;
196 al->data = (void *) ent->name;
197 al->ops = &devtree_symlink_inode_operations;
198 proc_register(de, al);
199 *lastp = al;
200 lastp = &al->next;
202 *lastp = 0;
203 de->subdir = list;
207 * Called on initialization to set up the /proc/device-tree subtree
209 void proc_device_tree_init(void)
211 struct device_node *root;
212 if ( !have_of )
213 return;
214 proc_device_tree = create_proc_entry("device-tree", S_IFDIR, 0);
215 if (proc_device_tree == 0)
216 return;
217 root = find_path_device("/");
218 if (root == 0) {
219 printk(KERN_ERR "/proc/device-tree: can't find root\n");
220 return;
222 add_node(root, proc_device_tree);