proc.c revision a8b0ac0873cd911b3bf3d6e2db98f80d0b158d56
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
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>
26#include <linux/pnpbios.h>
27#include <linux/init.h>
28
29#include <asm/uaccess.h>
30
31#include "pnpbios.h"
32
33static struct proc_dir_entry *proc_pnp = NULL;
34static struct proc_dir_entry *proc_pnp_boot = NULL;
35
36static int proc_read_pnpconfig(char *buf, char **start, off_t pos,
37			       int count, int *eof, void *data)
38{
39	struct pnp_isa_config_struc pnps;
40
41	if (pnp_bios_isapnp_config(&pnps))
42		return -EIO;
43	return snprintf(buf, count,
44			"structure_revision %d\n"
45			"number_of_CSNs %d\n"
46			"ISA_read_data_port 0x%x\n",
47			pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
48}
49
50static int proc_read_escdinfo(char *buf, char **start, off_t pos,
51			      int count, int *eof, void *data)
52{
53	struct escd_info_struc escd;
54
55	if (pnp_bios_escd_info(&escd))
56		return -EIO;
57	return snprintf(buf, count,
58			"min_ESCD_write_size %d\n"
59			"ESCD_size %d\n"
60			"NVRAM_base 0x%x\n",
61			escd.min_escd_write_size,
62			escd.escd_size, escd.nv_storage_base);
63}
64
65#define MAX_SANE_ESCD_SIZE (32*1024)
66static int proc_read_escd(char *buf, char **start, off_t pos,
67			  int count, int *eof, void *data)
68{
69	struct escd_info_struc escd;
70	char *tmpbuf;
71	int escd_size, escd_left_to_read, n;
72
73	if (pnp_bios_escd_info(&escd))
74		return -EIO;
75
76	/* sanity check */
77	if (escd.escd_size > MAX_SANE_ESCD_SIZE) {
78		printk(KERN_ERR
79		       "PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great\n");
80		return -EFBIG;
81	}
82
83	tmpbuf = kzalloc(escd.escd_size, GFP_KERNEL);
84	if (!tmpbuf)
85		return -ENOMEM;
86
87	if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) {
88		kfree(tmpbuf);
89		return -EIO;
90	}
91
92	escd_size =
93	    (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1]) * 256;
94
95	/* sanity check */
96	if (escd_size > MAX_SANE_ESCD_SIZE) {
97		printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by"
98				" BIOS read_escd call is too great\n");
99		kfree(tmpbuf);
100		return -EFBIG;
101	}
102
103	escd_left_to_read = escd_size - pos;
104	if (escd_left_to_read < 0)
105		escd_left_to_read = 0;
106	if (escd_left_to_read == 0)
107		*eof = 1;
108	n = min(count, escd_left_to_read);
109	memcpy(buf, tmpbuf + pos, n);
110	kfree(tmpbuf);
111	*start = buf;
112	return n;
113}
114
115static int proc_read_legacyres(char *buf, char **start, off_t pos,
116			       int count, int *eof, void *data)
117{
118	/* Assume that the following won't overflow the buffer */
119	if (pnp_bios_get_stat_res(buf))
120		return -EIO;
121
122	return count;		// FIXME: Return actual length
123}
124
125static int proc_read_devices(char *buf, char **start, off_t pos,
126			     int count, int *eof, void *data)
127{
128	struct pnp_bios_node *node;
129	u8 nodenum;
130	char *p = buf;
131
132	if (pos >= 0xff)
133		return 0;
134
135	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
136	if (!node)
137		return -ENOMEM;
138
139	for (nodenum = pos; nodenum < 0xff;) {
140		u8 thisnodenum = nodenum;
141		/* 26 = the number of characters per line sprintf'ed */
142		if ((p - buf + 26) > count)
143			break;
144		if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node))
145			break;
146		p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n",
147			     node->handle, node->eisa_id,
148			     node->type_code[0], node->type_code[1],
149			     node->type_code[2], node->flags);
150		if (nodenum <= thisnodenum) {
151			printk(KERN_ERR
152			       "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n",
153			       "PnPBIOS: proc_read_devices:",
154			       (unsigned int)nodenum,
155			       (unsigned int)thisnodenum);
156			*eof = 1;
157			break;
158		}
159	}
160	kfree(node);
161	if (nodenum == 0xff)
162		*eof = 1;
163	*start = (char *)((off_t) nodenum - pos);
164	return p - buf;
165}
166
167static int proc_read_node(char *buf, char **start, off_t pos,
168			  int count, int *eof, void *data)
169{
170	struct pnp_bios_node *node;
171	int boot = (long)data >> 8;
172	u8 nodenum = (long)data;
173	int len;
174
175	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
176	if (!node)
177		return -ENOMEM;
178	if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
179		kfree(node);
180		return -EIO;
181	}
182	len = node->size - sizeof(struct pnp_bios_node);
183	memcpy(buf, node->data, len);
184	kfree(node);
185	return len;
186}
187
188static int proc_write_node(struct file *file, const char __user * buf,
189			   unsigned long count, void *data)
190{
191	struct pnp_bios_node *node;
192	int boot = (long)data >> 8;
193	u8 nodenum = (long)data;
194	int ret = count;
195
196	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
197	if (!node)
198		return -ENOMEM;
199	if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
200		ret = -EIO;
201		goto out;
202	}
203	if (count != node->size - sizeof(struct pnp_bios_node)) {
204		ret = -EINVAL;
205		goto out;
206	}
207	if (copy_from_user(node->data, buf, count)) {
208		ret = -EFAULT;
209		goto out;
210	}
211	if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) {
212		ret = -EINVAL;
213		goto out;
214	}
215	ret = count;
216out:
217	kfree(node);
218	return ret;
219}
220
221int pnpbios_interface_attach_device(struct pnp_bios_node *node)
222{
223	char name[3];
224	struct proc_dir_entry *ent;
225
226	sprintf(name, "%02x", node->handle);
227
228	if (!proc_pnp)
229		return -EIO;
230	if (!pnpbios_dont_use_current_config) {
231		ent = create_proc_entry(name, 0, proc_pnp);
232		if (ent) {
233			ent->read_proc = proc_read_node;
234			ent->write_proc = proc_write_node;
235			ent->data = (void *)(long)(node->handle);
236		}
237	}
238
239	if (!proc_pnp_boot)
240		return -EIO;
241	ent = create_proc_entry(name, 0, proc_pnp_boot);
242	if (ent) {
243		ent->read_proc = proc_read_node;
244		ent->write_proc = proc_write_node;
245		ent->data = (void *)(long)(node->handle + 0x100);
246		return 0;
247	}
248
249	return -EIO;
250}
251
252/*
253 * When this is called, pnpbios functions are assumed to
254 * work and the pnpbios_dont_use_current_config flag
255 * should already have been set to the appropriate value
256 */
257int __init pnpbios_proc_init(void)
258{
259	proc_pnp = proc_mkdir("pnp", proc_bus);
260	if (!proc_pnp)
261		return -EIO;
262	proc_pnp_boot = proc_mkdir("boot", proc_pnp);
263	if (!proc_pnp_boot)
264		return -EIO;
265	create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL);
266	create_proc_read_entry("configuration_info", 0, proc_pnp,
267			       proc_read_pnpconfig, NULL);
268	create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo,
269			       NULL);
270	create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL);
271	create_proc_read_entry("legacy_device_resources", 0, proc_pnp,
272			       proc_read_legacyres, NULL);
273
274	return 0;
275}
276
277void __exit pnpbios_proc_exit(void)
278{
279	int i;
280	char name[3];
281
282	if (!proc_pnp)
283		return;
284
285	for (i = 0; i < 0xff; i++) {
286		sprintf(name, "%02x", i);
287		if (!pnpbios_dont_use_current_config)
288			remove_proc_entry(name, proc_pnp);
289		remove_proc_entry(name, proc_pnp_boot);
290	}
291	remove_proc_entry("legacy_device_resources", proc_pnp);
292	remove_proc_entry("escd", proc_pnp);
293	remove_proc_entry("escd_info", proc_pnp);
294	remove_proc_entry("configuration_info", proc_pnp);
295	remove_proc_entry("devices", proc_pnp);
296	remove_proc_entry("boot", proc_pnp);
297	remove_proc_entry("pnp", proc_bus);
298}
299