Blame view

fs/proc/proc_devtree.c 5.27 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
  /*
   * proc_devtree.c - handles /proc/device-tree
   *
   * Copyright 1997 Paul Mackerras
   */
  #include <linux/errno.h>
1e0edd3f6   Alexey Dobriyan   proc: spread __init
7
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
  #include <linux/time.h>
  #include <linux/proc_fs.h>
e22f62839   Alexey Dobriyan   Convert /proc/dev...
10
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  #include <linux/stat.h>
  #include <linux/string.h>
50ab2fe14   Jeremy Kerr   proc_devtree: inc...
13
  #include <linux/of.h>
7c540d9e3   Jeremy Kerr   proc_devtree: fix...
14
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
  #include <asm/prom.h>
  #include <asm/uaccess.h>
3174c21b7   Al Viro   Move junk from pr...
18
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
20
21
  static inline void set_node_proc_entry(struct device_node *np,
  				       struct proc_dir_entry *de)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  {
8cfb3343f   Jeremy Kerr   of: make set_node...
23
24
  #ifdef HAVE_ARCH_DEVTREE_FIXUPS
  	np->pde = de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #endif
8cfb3343f   Jeremy Kerr   of: make set_node...
26
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
  
  static struct proc_dir_entry *proc_device_tree;
  
  /*
   * Supply data on a read from /proc/device-tree/node/property.
   */
e22f62839   Alexey Dobriyan   Convert /proc/dev...
33
  static int property_proc_show(struct seq_file *m, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  {
e22f62839   Alexey Dobriyan   Convert /proc/dev...
35
  	struct property *pp = m->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

e22f62839   Alexey Dobriyan   Convert /proc/dev...
37
38
  	seq_write(m, pp->value, pp->length);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  }
e22f62839   Alexey Dobriyan   Convert /proc/dev...
40
41
42
43
44
45
46
47
48
49
50
51
  static int property_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, property_proc_show, PDE(inode)->data);
  }
  
  static const struct file_operations property_proc_fops = {
  	.owner		= THIS_MODULE,
  	.open		= property_proc_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
  /*
   * For a node with a name like "gc@10", we make symlinks called "gc"
   * and "@10" to it.
   */
  
  /*
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
58
59
60
   * Add a property to a node
   */
  static struct proc_dir_entry *
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
61
62
  __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp,
  		const char *name)
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
63
64
65
66
67
68
69
  {
  	struct proc_dir_entry *ent;
  
  	/*
  	 * Unfortunately proc_register puts each new entry
  	 * at the beginning of the list.  So we rearrange them.
  	 */
e22f62839   Alexey Dobriyan   Convert /proc/dev...
70
71
72
  	ent = proc_create_data(name,
  			       strncmp(name, "security-", 9) ? S_IRUGO : S_IRUSR,
  			       de, &property_proc_fops, pp);
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
73
74
  	if (ent == NULL)
  		return NULL;
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
75
  	if (!strncmp(name, "security-", 9))
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
76
77
78
79
80
81
82
83
84
85
  		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: ...
86
  	__proc_device_tree_add_prop(pde, prop, prop->name);
183d02025   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
87
  }
898b5395e   Dave C Boutcher   [PATCH] powerpc: ...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  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...
112
  /*
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
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
173
174
175
176
177
   * 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
178
179
   * Process a node, adding entries for its children and its properties.
   */
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
180
181
  void proc_device_tree_add_node(struct device_node *np,
  			       struct proc_dir_entry *de)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
  {
  	struct property *pp;
  	struct proc_dir_entry *ent;
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
185
  	struct device_node *child;
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
186
  	const char *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  
  	set_node_proc_entry(np, de);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
189
  	for (child = NULL; (child = of_get_next_child(np, child));) {
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
190
  		/* Use everything after the last slash, or the full name */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
  		p = strrchr(child->full_name, '/');
  		if (!p)
  			p = child->full_name;
  		else
  			++p;
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
196
197
198
  
  		if (duplicate_name(de, p))
  			p = fixup_name(np, de, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  		ent = proc_mkdir(p, de);
bcac2b1b7   Michal Simek   procfs: remove sp...
200
  		if (ent == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  		proc_device_tree_add_node(child, ent);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
203
204
  	}
  	of_node_put(child);
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
205

bcac2b1b7   Michal Simek   procfs: remove sp...
206
  	for (pp = np->properties; pp != NULL; pp = pp->next) {
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
207
  		p = pp->name;
9f069af5b   Michael Ellerman   of: Drop properti...
208
209
  		if (strchr(p, '/'))
  			continue;
5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
210
211
  		if (duplicate_name(de, p))
  			p = fixup_name(np, de, p);
5f64f7395   Benjamin Herrenschmidt   [PATCH] ppc32/ppc...
212

5149fa47e   Michael Ellerman   [PATCH] powerpc: ...
213
  		ent = __proc_device_tree_add_prop(de, pp, p);
bcac2b1b7   Michal Simek   procfs: remove sp...
214
  		if (ent == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
  }
  
  /*
   * Called on initialization to set up the /proc/device-tree subtree
   */
1e0edd3f6   Alexey Dobriyan   proc: spread __init
222
  void __init proc_device_tree_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  {
  	struct device_node *root;
6b82b3e4b   Anton Vorontsov   powerpc: Remove `...
225

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	proc_device_tree = proc_mkdir("device-tree", NULL);
bcac2b1b7   Michal Simek   procfs: remove sp...
227
  	if (proc_device_tree == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
  		return;
  	root = of_find_node_by_path("/");
bcac2b1b7   Michal Simek   procfs: remove sp...
230
  	if (root == NULL) {
8aaccf7fa   Paul Bolle   of/flattree: Drop...
231
232
  		pr_debug("/proc/device-tree: can't find root
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
  		return;
  	}
  	proc_device_tree_add_node(root, proc_device_tree);
  	of_node_put(root);
  }