]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | } |