Blame view

fs/proc/proc_devtree.c 4.97 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * proc_devtree.c - handles /proc/device-tree
   *
   * Copyright 1997 Paul Mackerras
   */
  #include <linux/errno.h>
  #include <linux/time.h>
  #include <linux/proc_fs.h>
  #include <linux/stat.h>
  #include <linux/string.h>
  #include <asm/prom.h>
  #include <asm/uaccess.h>
  
  #ifndef HAVE_ARCH_DEVTREE_FIXUPS
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
15
16
  static inline void set_node_proc_entry(struct device_node *np,
  				       struct proc_dir_entry *de)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  {
  }
  #endif
  
  static struct proc_dir_entry *proc_device_tree;
  
  /*
   * Supply data on a read from /proc/device-tree/node/property.
   */
  static int property_read_proc(char *page, char **start, off_t off,
  			      int count, int *eof, void *data)
  {
  	struct property *pp = data;
  	int n;
  
  	if (off >= pp->length) {
  		*eof = 1;
  		return 0;
  	}
  	n = pp->length - off;
  	if (n > count)
  		n = count;
  	else
  		*eof = 1;
1a38147ed   Stephen Rothwell   [POWERPC] Make st...
41
  	memcpy(page, (char *)pp->value + off, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
50
51
  	*start = page;
  	return n;
  }
  
  /*
   * For a node with a name like "gc@10", we make symlinks called "gc"
   * and "@10" to it.
   */
  
  /*
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
52
53
54
   * Add a property to a node
   */
  static struct proc_dir_entry *
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
55
56
  __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp,
  		const char *name)
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
57
58
59
60
61
62
63
  {
  	struct proc_dir_entry *ent;
  
  	/*
  	 * Unfortunately proc_register puts each new entry
  	 * at the beginning of the list.  So we rearrange them.
  	 */
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
64
65
  	ent = create_proc_read_entry(name,
  				     strncmp(name, "security-", 9)
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
66
67
68
69
  				     ? S_IRUGO : S_IRUSR, de,
  				     property_read_proc, pp);
  	if (ent == NULL)
  		return NULL;
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
70
  	if (!strncmp(name, "security-", 9))
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
71
72
73
74
75
76
77
78
79
80
  		ent->size = 0; /* don't leak number of password chars */
  	else
  		ent->size = pp->length;
  
  	return ent;
  }
  
  
  void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
  {
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
81
  	__proc_device_tree_add_prop(pde, prop, prop->name);
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
82
  }
898b5395e   Dave C Boutcher   [PATCH] powerpc: ...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
  				  struct property *prop)
  {
  	remove_proc_entry(prop->name, pde);
  }
  
  void proc_device_tree_update_prop(struct proc_dir_entry *pde,
  				  struct property *newprop,
  				  struct property *oldprop)
  {
  	struct proc_dir_entry *ent;
  
  	for (ent = pde->subdir; ent != NULL; ent = ent->next)
  		if (ent->data == oldprop)
  			break;
  	if (ent == NULL) {
  		printk(KERN_WARNING "device-tree: property \"%s\" "
  		       " does not exist
  ", oldprop->name);
  	} else {
  		ent->data = newprop;
  		ent->size = newprop->length;
  	}
  }
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
107
  /*
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
   * Various dodgy firmware might give us nodes and/or properties with
   * conflicting names. That's generally ok, except for exporting via /proc,
   * so munge names here to ensure they're unique.
   */
  
  static int duplicate_name(struct proc_dir_entry *de, const char *name)
  {
  	struct proc_dir_entry *ent;
  	int found = 0;
  
  	spin_lock(&proc_subdir_lock);
  
  	for (ent = de->subdir; ent != NULL; ent = ent->next) {
  		if (strcmp(ent->name, name) == 0) {
  			found = 1;
  			break;
  		}
  	}
  
  	spin_unlock(&proc_subdir_lock);
  
  	return found;
  }
  
  static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de,
  		const char *name)
  {
  	char *fixed_name;
  	int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */
  	int i = 1, size;
  
  realloc:
  	fixed_name = kmalloc(fixup_len, GFP_KERNEL);
  	if (fixed_name == NULL) {
  		printk(KERN_ERR "device-tree: Out of memory trying to fixup "
  				"name \"%s\"
  ", name);
  		return name;
  	}
  
  retry:
  	size = snprintf(fixed_name, fixup_len, "%s#%d", name, i);
  	size++; /* account for NULL */
  
  	if (size > fixup_len) {
  		/* We ran out of space, free and reallocate. */
  		kfree(fixed_name);
  		fixup_len = size;
  		goto realloc;
  	}
  
  	if (duplicate_name(de, fixed_name)) {
  		/* Multiple duplicates. Retry with a different offset. */
  		i++;
  		goto retry;
  	}
  
  	printk(KERN_WARNING "device-tree: Duplicate name in %s, "
  			"renamed to \"%s\"
  ", np->full_name, fixed_name);
  
  	return fixed_name;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
   * Process a node, adding entries for its children and its properties.
   */
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
175
176
  void proc_device_tree_add_node(struct device_node *np,
  			       struct proc_dir_entry *de)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  {
  	struct property *pp;
  	struct proc_dir_entry *ent;
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
180
  	struct device_node *child;
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
181
  	const char *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  
  	set_node_proc_entry(np, de);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
184
  	for (child = NULL; (child = of_get_next_child(np, child));) {
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
185
  		/* Use everything after the last slash, or the full name */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
  		p = strrchr(child->full_name, '/');
  		if (!p)
  			p = child->full_name;
  		else
  			++p;
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
191
192
193
  
  		if (duplicate_name(de, p))
  			p = fixup_name(np, de, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
  		ent = proc_mkdir(p, de);
  		if (ent == 0)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  		proc_device_tree_add_node(child, ent);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
198
199
  	}
  	of_node_put(child);
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
200

5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
201
  	for (pp = np->properties; pp != 0; pp = pp->next) {
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
202
203
204
205
  		p = pp->name;
  
  		if (duplicate_name(de, p))
  			p = fixup_name(np, de, p);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
206

5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
207
  		ent = __proc_device_tree_add_prop(de, pp, p);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
208
  		if (ent == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  }
  
  /*
   * Called on initialization to set up the /proc/device-tree subtree
   */
  void proc_device_tree_init(void)
  {
  	struct device_node *root;
  	if ( !have_of )
  		return;
  	proc_device_tree = proc_mkdir("device-tree", NULL);
  	if (proc_device_tree == 0)
  		return;
  	root = of_find_node_by_path("/");
  	if (root == 0) {
  		printk(KERN_ERR "/proc/device-tree: can't find root
  ");
  		return;
  	}
  	proc_device_tree_add_node(root, proc_device_tree);
  	of_node_put(root);
  }