Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * /proc/bus/pnp interface for Plug and Play devices | |
3 | * | |
4 | * Written by David Hinds, dahinds@users.sourceforge.net | |
5 | * Modified by Thomas Hood | |
6 | * | |
7 | * The .../devices and .../<node> and .../boot/<node> files are | |
8 | * utilized by the lspnp and setpnp utilities, supplied with the | |
9 | * pcmcia-cs package. | |
10 | * http://pcmcia-cs.sourceforge.net | |
11 | * | |
12 | * The .../escd file is utilized by the lsescd utility written by | |
13 | * Gunther Mayer. | |
14 | * http://home.t-online.de/home/gunther.mayer/lsescd | |
15 | * | |
16 | * The .../legacy_device_resources file is not used yet. | |
17 | * | |
18 | * The other files are human-readable. | |
19 | */ | |
20 | ||
1da177e4 LT |
21 | #include <linux/module.h> |
22 | #include <linux/kernel.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/types.h> | |
25 | #include <linux/proc_fs.h> | |
dfd2e1b4 | 26 | #include <linux/pnp.h> |
5116fa2b | 27 | #include <linux/seq_file.h> |
1da177e4 LT |
28 | #include <linux/init.h> |
29 | ||
30 | #include <asm/uaccess.h> | |
31 | ||
32 | #include "pnpbios.h" | |
33 | ||
34 | static struct proc_dir_entry *proc_pnp = NULL; | |
35 | static struct proc_dir_entry *proc_pnp_boot = NULL; | |
36 | ||
5116fa2b | 37 | static int pnpconfig_proc_show(struct seq_file *m, void *v) |
1da177e4 LT |
38 | { |
39 | struct pnp_isa_config_struc pnps; | |
40 | ||
41 | if (pnp_bios_isapnp_config(&pnps)) | |
42 | return -EIO; | |
5116fa2b AD |
43 | seq_printf(m, "structure_revision %d\n" |
44 | "number_of_CSNs %d\n" | |
45 | "ISA_read_data_port 0x%x\n", | |
46 | pnps.revision, pnps.no_csns, pnps.isa_rd_data_port); | |
47 | return 0; | |
1da177e4 LT |
48 | } |
49 | ||
5116fa2b AD |
50 | static int pnpconfig_proc_open(struct inode *inode, struct file *file) |
51 | { | |
52 | return single_open(file, pnpconfig_proc_show, NULL); | |
53 | } | |
54 | ||
55 | static const struct file_operations pnpconfig_proc_fops = { | |
56 | .owner = THIS_MODULE, | |
57 | .open = pnpconfig_proc_open, | |
58 | .read = seq_read, | |
59 | .llseek = seq_lseek, | |
60 | .release = single_release, | |
61 | }; | |
62 | ||
63 | static int escd_info_proc_show(struct seq_file *m, void *v) | |
1da177e4 LT |
64 | { |
65 | struct escd_info_struc escd; | |
66 | ||
67 | if (pnp_bios_escd_info(&escd)) | |
68 | return -EIO; | |
5116fa2b | 69 | seq_printf(m, "min_ESCD_write_size %d\n" |
9dd78466 BH |
70 | "ESCD_size %d\n" |
71 | "NVRAM_base 0x%x\n", | |
72 | escd.min_escd_write_size, | |
73 | escd.escd_size, escd.nv_storage_base); | |
5116fa2b | 74 | return 0; |
1da177e4 LT |
75 | } |
76 | ||
5116fa2b AD |
77 | static int escd_info_proc_open(struct inode *inode, struct file *file) |
78 | { | |
79 | return single_open(file, escd_info_proc_show, NULL); | |
80 | } | |
81 | ||
82 | static const struct file_operations escd_info_proc_fops = { | |
83 | .owner = THIS_MODULE, | |
84 | .open = escd_info_proc_open, | |
85 | .read = seq_read, | |
86 | .llseek = seq_lseek, | |
87 | .release = single_release, | |
88 | }; | |
89 | ||
1da177e4 | 90 | #define MAX_SANE_ESCD_SIZE (32*1024) |
5116fa2b | 91 | static int escd_proc_show(struct seq_file *m, void *v) |
1da177e4 LT |
92 | { |
93 | struct escd_info_struc escd; | |
94 | char *tmpbuf; | |
5116fa2b | 95 | int escd_size; |
1da177e4 LT |
96 | |
97 | if (pnp_bios_escd_info(&escd)) | |
98 | return -EIO; | |
99 | ||
100 | /* sanity check */ | |
101 | if (escd.escd_size > MAX_SANE_ESCD_SIZE) { | |
9dd78466 | 102 | printk(KERN_ERR |
5116fa2b | 103 | "PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__); |
1da177e4 LT |
104 | return -EFBIG; |
105 | } | |
106 | ||
cd861280 | 107 | tmpbuf = kzalloc(escd.escd_size, GFP_KERNEL); |
9dd78466 BH |
108 | if (!tmpbuf) |
109 | return -ENOMEM; | |
1da177e4 LT |
110 | |
111 | if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) { | |
112 | kfree(tmpbuf); | |
113 | return -EIO; | |
114 | } | |
115 | ||
9dd78466 BH |
116 | escd_size = |
117 | (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1]) * 256; | |
1da177e4 LT |
118 | |
119 | /* sanity check */ | |
120 | if (escd_size > MAX_SANE_ESCD_SIZE) { | |
5116fa2b AD |
121 | printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by" |
122 | " BIOS read_escd call is too great\n", __func__); | |
a8b0ac08 | 123 | kfree(tmpbuf); |
1da177e4 LT |
124 | return -EFBIG; |
125 | } | |
126 | ||
5116fa2b | 127 | seq_write(m, tmpbuf, escd_size); |
1da177e4 | 128 | kfree(tmpbuf); |
5116fa2b | 129 | return 0; |
1da177e4 LT |
130 | } |
131 | ||
5116fa2b AD |
132 | static int escd_proc_open(struct inode *inode, struct file *file) |
133 | { | |
134 | return single_open(file, escd_proc_show, NULL); | |
135 | } | |
136 | ||
137 | static const struct file_operations escd_proc_fops = { | |
138 | .owner = THIS_MODULE, | |
139 | .open = escd_proc_open, | |
140 | .read = seq_read, | |
141 | .llseek = seq_lseek, | |
142 | .release = single_release, | |
143 | }; | |
144 | ||
145 | static int pnp_legacyres_proc_show(struct seq_file *m, void *v) | |
1da177e4 | 146 | { |
5116fa2b AD |
147 | void *buf; |
148 | ||
149 | buf = kmalloc(65536, GFP_KERNEL); | |
150 | if (!buf) | |
151 | return -ENOMEM; | |
152 | if (pnp_bios_get_stat_res(buf)) { | |
153 | kfree(buf); | |
1da177e4 | 154 | return -EIO; |
5116fa2b AD |
155 | } |
156 | ||
157 | seq_write(m, buf, 65536); | |
158 | kfree(buf); | |
159 | return 0; | |
160 | } | |
1da177e4 | 161 | |
5116fa2b AD |
162 | static int pnp_legacyres_proc_open(struct inode *inode, struct file *file) |
163 | { | |
164 | return single_open(file, pnp_legacyres_proc_show, NULL); | |
1da177e4 LT |
165 | } |
166 | ||
5116fa2b AD |
167 | static const struct file_operations pnp_legacyres_proc_fops = { |
168 | .owner = THIS_MODULE, | |
169 | .open = pnp_legacyres_proc_open, | |
170 | .read = seq_read, | |
171 | .llseek = seq_lseek, | |
172 | .release = single_release, | |
173 | }; | |
174 | ||
175 | static int pnp_devices_proc_show(struct seq_file *m, void *v) | |
1da177e4 LT |
176 | { |
177 | struct pnp_bios_node *node; | |
178 | u8 nodenum; | |
1da177e4 | 179 | |
cd861280 | 180 | node = kzalloc(node_info.max_node_size, GFP_KERNEL); |
9dd78466 BH |
181 | if (!node) |
182 | return -ENOMEM; | |
1da177e4 | 183 | |
5116fa2b | 184 | for (nodenum = 0; nodenum < 0xff;) { |
1da177e4 | 185 | u8 thisnodenum = nodenum; |
5116fa2b | 186 | |
1da177e4 LT |
187 | if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node)) |
188 | break; | |
5116fa2b | 189 | seq_printf(m, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", |
1da177e4 LT |
190 | node->handle, node->eisa_id, |
191 | node->type_code[0], node->type_code[1], | |
192 | node->type_code[2], node->flags); | |
193 | if (nodenum <= thisnodenum) { | |
9dd78466 BH |
194 | printk(KERN_ERR |
195 | "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", | |
196 | "PnPBIOS: proc_read_devices:", | |
197 | (unsigned int)nodenum, | |
198 | (unsigned int)thisnodenum); | |
1da177e4 LT |
199 | break; |
200 | } | |
201 | } | |
202 | kfree(node); | |
5116fa2b AD |
203 | return 0; |
204 | } | |
205 | ||
206 | static int pnp_devices_proc_open(struct inode *inode, struct file *file) | |
207 | { | |
208 | return single_open(file, pnp_devices_proc_show, NULL); | |
1da177e4 LT |
209 | } |
210 | ||
5116fa2b AD |
211 | static const struct file_operations pnp_devices_proc_fops = { |
212 | .owner = THIS_MODULE, | |
213 | .open = pnp_devices_proc_open, | |
214 | .read = seq_read, | |
215 | .llseek = seq_lseek, | |
216 | .release = single_release, | |
217 | }; | |
218 | ||
219 | static int pnpbios_proc_show(struct seq_file *m, void *v) | |
1da177e4 | 220 | { |
5116fa2b | 221 | void *data = m->private; |
1da177e4 LT |
222 | struct pnp_bios_node *node; |
223 | int boot = (long)data >> 8; | |
224 | u8 nodenum = (long)data; | |
225 | int len; | |
226 | ||
cd861280 | 227 | node = kzalloc(node_info.max_node_size, GFP_KERNEL); |
9dd78466 BH |
228 | if (!node) |
229 | return -ENOMEM; | |
1da177e4 LT |
230 | if (pnp_bios_get_dev_node(&nodenum, boot, node)) { |
231 | kfree(node); | |
232 | return -EIO; | |
233 | } | |
234 | len = node->size - sizeof(struct pnp_bios_node); | |
5116fa2b | 235 | seq_write(m, node->data, len); |
1da177e4 | 236 | kfree(node); |
5116fa2b AD |
237 | return 0; |
238 | } | |
239 | ||
240 | static int pnpbios_proc_open(struct inode *inode, struct file *file) | |
241 | { | |
242 | return single_open(file, pnpbios_proc_show, PDE(inode)->data); | |
1da177e4 LT |
243 | } |
244 | ||
5116fa2b AD |
245 | static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf, |
246 | size_t count, loff_t *pos) | |
1da177e4 | 247 | { |
5116fa2b | 248 | void *data = PDE(file->f_path.dentry->d_inode)->data; |
1da177e4 LT |
249 | struct pnp_bios_node *node; |
250 | int boot = (long)data >> 8; | |
251 | u8 nodenum = (long)data; | |
252 | int ret = count; | |
253 | ||
cd861280 | 254 | node = kzalloc(node_info.max_node_size, GFP_KERNEL); |
1da177e4 LT |
255 | if (!node) |
256 | return -ENOMEM; | |
257 | if (pnp_bios_get_dev_node(&nodenum, boot, node)) { | |
258 | ret = -EIO; | |
259 | goto out; | |
260 | } | |
261 | if (count != node->size - sizeof(struct pnp_bios_node)) { | |
262 | ret = -EINVAL; | |
263 | goto out; | |
264 | } | |
265 | if (copy_from_user(node->data, buf, count)) { | |
266 | ret = -EFAULT; | |
267 | goto out; | |
268 | } | |
269 | if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) { | |
270 | ret = -EINVAL; | |
271 | goto out; | |
272 | } | |
273 | ret = count; | |
1e0aa9ad | 274 | out: |
1da177e4 LT |
275 | kfree(node); |
276 | return ret; | |
277 | } | |
278 | ||
5116fa2b AD |
279 | static const struct file_operations pnpbios_proc_fops = { |
280 | .owner = THIS_MODULE, | |
281 | .open = pnpbios_proc_open, | |
282 | .read = seq_read, | |
283 | .llseek = seq_lseek, | |
284 | .release = single_release, | |
285 | .write = pnpbios_proc_write, | |
286 | }; | |
287 | ||
9dd78466 | 288 | int pnpbios_interface_attach_device(struct pnp_bios_node *node) |
1da177e4 LT |
289 | { |
290 | char name[3]; | |
1da177e4 LT |
291 | |
292 | sprintf(name, "%02x", node->handle); | |
293 | ||
294 | if (!proc_pnp) | |
295 | return -EIO; | |
9dd78466 | 296 | if (!pnpbios_dont_use_current_config) { |
5116fa2b AD |
297 | proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_fops, |
298 | (void *)(long)(node->handle)); | |
1da177e4 LT |
299 | } |
300 | ||
301 | if (!proc_pnp_boot) | |
302 | return -EIO; | |
5116fa2b AD |
303 | if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_fops, |
304 | (void *)(long)(node->handle + 0x100))) | |
1da177e4 | 305 | return 0; |
1da177e4 LT |
306 | return -EIO; |
307 | } | |
308 | ||
309 | /* | |
310 | * When this is called, pnpbios functions are assumed to | |
311 | * work and the pnpbios_dont_use_current_config flag | |
312 | * should already have been set to the appropriate value | |
313 | */ | |
9dd78466 | 314 | int __init pnpbios_proc_init(void) |
1da177e4 | 315 | { |
9c37066d | 316 | proc_pnp = proc_mkdir("bus/pnp", NULL); |
1da177e4 LT |
317 | if (!proc_pnp) |
318 | return -EIO; | |
319 | proc_pnp_boot = proc_mkdir("boot", proc_pnp); | |
320 | if (!proc_pnp_boot) | |
321 | return -EIO; | |
5116fa2b AD |
322 | proc_create("devices", 0, proc_pnp, &pnp_devices_proc_fops); |
323 | proc_create("configuration_info", 0, proc_pnp, &pnpconfig_proc_fops); | |
324 | proc_create("escd_info", 0, proc_pnp, &escd_info_proc_fops); | |
325 | proc_create("escd", S_IRUSR, proc_pnp, &escd_proc_fops); | |
326 | proc_create("legacy_device_resources", 0, proc_pnp, &pnp_legacyres_proc_fops); | |
1da177e4 LT |
327 | |
328 | return 0; | |
329 | } | |
330 | ||
331 | void __exit pnpbios_proc_exit(void) | |
332 | { | |
333 | int i; | |
334 | char name[3]; | |
335 | ||
336 | if (!proc_pnp) | |
337 | return; | |
338 | ||
9dd78466 | 339 | for (i = 0; i < 0xff; i++) { |
1da177e4 | 340 | sprintf(name, "%02x", i); |
9dd78466 | 341 | if (!pnpbios_dont_use_current_config) |
1da177e4 LT |
342 | remove_proc_entry(name, proc_pnp); |
343 | remove_proc_entry(name, proc_pnp_boot); | |
344 | } | |
345 | remove_proc_entry("legacy_device_resources", proc_pnp); | |
346 | remove_proc_entry("escd", proc_pnp); | |
347 | remove_proc_entry("escd_info", proc_pnp); | |
348 | remove_proc_entry("configuration_info", proc_pnp); | |
349 | remove_proc_entry("devices", proc_pnp); | |
350 | remove_proc_entry("boot", proc_pnp); | |
9c37066d | 351 | remove_proc_entry("bus/pnp", NULL); |
1da177e4 | 352 | } |