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