Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * /proc/bus/pnp interface for Plug and Play devices
0004  *
0005  * Written by David Hinds, dahinds@users.sourceforge.net
0006  * Modified by Thomas Hood
0007  *
0008  * The .../devices and .../<node> and .../boot/<node> files are
0009  * utilized by the lspnp and setpnp utilities, supplied with the
0010  * pcmcia-cs package.
0011  *     http://pcmcia-cs.sourceforge.net
0012  *
0013  * The .../escd file is utilized by the lsescd utility written by
0014  * Gunther Mayer.
0015  *
0016  * The .../legacy_device_resources file is not used yet.
0017  *
0018  * The other files are human-readable.
0019  */
0020 
0021 #include <linux/module.h>
0022 #include <linux/kernel.h>
0023 #include <linux/slab.h>
0024 #include <linux/types.h>
0025 #include <linux/proc_fs.h>
0026 #include <linux/pnp.h>
0027 #include <linux/seq_file.h>
0028 #include <linux/init.h>
0029 
0030 #include <linux/uaccess.h>
0031 
0032 #include "pnpbios.h"
0033 
0034 static struct proc_dir_entry *proc_pnp = NULL;
0035 static struct proc_dir_entry *proc_pnp_boot = NULL;
0036 
0037 static int pnpconfig_proc_show(struct seq_file *m, void *v)
0038 {
0039     struct pnp_isa_config_struc pnps;
0040 
0041     if (pnp_bios_isapnp_config(&pnps))
0042         return -EIO;
0043     seq_printf(m, "structure_revision %d\n"
0044               "number_of_CSNs %d\n"
0045               "ISA_read_data_port 0x%x\n",
0046            pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
0047     return 0;
0048 }
0049 
0050 static int escd_info_proc_show(struct seq_file *m, void *v)
0051 {
0052     struct escd_info_struc escd;
0053 
0054     if (pnp_bios_escd_info(&escd))
0055         return -EIO;
0056     seq_printf(m, "min_ESCD_write_size %d\n"
0057             "ESCD_size %d\n"
0058             "NVRAM_base 0x%x\n",
0059             escd.min_escd_write_size,
0060             escd.escd_size, escd.nv_storage_base);
0061     return 0;
0062 }
0063 
0064 #define MAX_SANE_ESCD_SIZE (32*1024)
0065 static int escd_proc_show(struct seq_file *m, void *v)
0066 {
0067     struct escd_info_struc escd;
0068     char *tmpbuf;
0069     int escd_size;
0070 
0071     if (pnp_bios_escd_info(&escd))
0072         return -EIO;
0073 
0074     /* sanity check */
0075     if (escd.escd_size > MAX_SANE_ESCD_SIZE) {
0076         printk(KERN_ERR
0077                "PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__);
0078         return -EFBIG;
0079     }
0080 
0081     tmpbuf = kzalloc(escd.escd_size, GFP_KERNEL);
0082     if (!tmpbuf)
0083         return -ENOMEM;
0084 
0085     if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) {
0086         kfree(tmpbuf);
0087         return -EIO;
0088     }
0089 
0090     escd_size =
0091         (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1]) * 256;
0092 
0093     /* sanity check */
0094     if (escd_size > MAX_SANE_ESCD_SIZE) {
0095         printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by"
0096                 " BIOS read_escd call is too great\n", __func__);
0097         kfree(tmpbuf);
0098         return -EFBIG;
0099     }
0100 
0101     seq_write(m, tmpbuf, escd_size);
0102     kfree(tmpbuf);
0103     return 0;
0104 }
0105 
0106 static int pnp_legacyres_proc_show(struct seq_file *m, void *v)
0107 {
0108     void *buf;
0109 
0110     buf = kmalloc(65536, GFP_KERNEL);
0111     if (!buf)
0112         return -ENOMEM;
0113     if (pnp_bios_get_stat_res(buf)) {
0114         kfree(buf);
0115         return -EIO;
0116     }
0117 
0118     seq_write(m, buf, 65536);
0119     kfree(buf);
0120     return 0;
0121 }
0122 
0123 static int pnp_devices_proc_show(struct seq_file *m, void *v)
0124 {
0125     struct pnp_bios_node *node;
0126     u8 nodenum;
0127 
0128     node = kzalloc(node_info.max_node_size, GFP_KERNEL);
0129     if (!node)
0130         return -ENOMEM;
0131 
0132     for (nodenum = 0; nodenum < 0xff;) {
0133         u8 thisnodenum = nodenum;
0134 
0135         if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node))
0136             break;
0137         seq_printf(m, "%02x\t%08x\t%3phC\t%04x\n",
0138                  node->handle, node->eisa_id,
0139                  node->type_code, node->flags);
0140         if (nodenum <= thisnodenum) {
0141             printk(KERN_ERR
0142                    "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n",
0143                    "PnPBIOS: proc_read_devices:",
0144                    (unsigned int)nodenum,
0145                    (unsigned int)thisnodenum);
0146             break;
0147         }
0148     }
0149     kfree(node);
0150     return 0;
0151 }
0152 
0153 static int pnpbios_proc_show(struct seq_file *m, void *v)
0154 {
0155     void *data = m->private;
0156     struct pnp_bios_node *node;
0157     int boot = (long)data >> 8;
0158     u8 nodenum = (long)data;
0159     int len;
0160 
0161     node = kzalloc(node_info.max_node_size, GFP_KERNEL);
0162     if (!node)
0163         return -ENOMEM;
0164     if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
0165         kfree(node);
0166         return -EIO;
0167     }
0168     len = node->size - sizeof(struct pnp_bios_node);
0169     seq_write(m, node->data, len);
0170     kfree(node);
0171     return 0;
0172 }
0173 
0174 static int pnpbios_proc_open(struct inode *inode, struct file *file)
0175 {
0176     return single_open(file, pnpbios_proc_show, pde_data(inode));
0177 }
0178 
0179 static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
0180                   size_t count, loff_t *pos)
0181 {
0182     void *data = pde_data(file_inode(file));
0183     struct pnp_bios_node *node;
0184     int boot = (long)data >> 8;
0185     u8 nodenum = (long)data;
0186     int ret = count;
0187 
0188     node = kzalloc(node_info.max_node_size, GFP_KERNEL);
0189     if (!node)
0190         return -ENOMEM;
0191     if (pnp_bios_get_dev_node(&nodenum, boot, node)) {
0192         ret = -EIO;
0193         goto out;
0194     }
0195     if (count != node->size - sizeof(struct pnp_bios_node)) {
0196         ret = -EINVAL;
0197         goto out;
0198     }
0199     if (copy_from_user(node->data, buf, count)) {
0200         ret = -EFAULT;
0201         goto out;
0202     }
0203     if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) {
0204         ret = -EINVAL;
0205         goto out;
0206     }
0207     ret = count;
0208 out:
0209     kfree(node);
0210     return ret;
0211 }
0212 
0213 static const struct proc_ops pnpbios_proc_ops = {
0214     .proc_open  = pnpbios_proc_open,
0215     .proc_read  = seq_read,
0216     .proc_lseek = seq_lseek,
0217     .proc_release   = single_release,
0218     .proc_write = pnpbios_proc_write,
0219 };
0220 
0221 int pnpbios_interface_attach_device(struct pnp_bios_node *node)
0222 {
0223     char name[3];
0224 
0225     sprintf(name, "%02x", node->handle);
0226 
0227     if (!proc_pnp)
0228         return -EIO;
0229     if (!pnpbios_dont_use_current_config) {
0230         proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_ops,
0231                  (void *)(long)(node->handle));
0232     }
0233 
0234     if (!proc_pnp_boot)
0235         return -EIO;
0236     if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_ops,
0237                  (void *)(long)(node->handle + 0x100)))
0238         return 0;
0239     return -EIO;
0240 }
0241 
0242 /*
0243  * When this is called, pnpbios functions are assumed to
0244  * work and the pnpbios_dont_use_current_config flag
0245  * should already have been set to the appropriate value
0246  */
0247 int __init pnpbios_proc_init(void)
0248 {
0249     proc_pnp = proc_mkdir("bus/pnp", NULL);
0250     if (!proc_pnp)
0251         return -EIO;
0252     proc_pnp_boot = proc_mkdir("boot", proc_pnp);
0253     if (!proc_pnp_boot)
0254         return -EIO;
0255     proc_create_single("devices", 0, proc_pnp, pnp_devices_proc_show);
0256     proc_create_single("configuration_info", 0, proc_pnp,
0257             pnpconfig_proc_show);
0258     proc_create_single("escd_info", 0, proc_pnp, escd_info_proc_show);
0259     proc_create_single("escd", S_IRUSR, proc_pnp, escd_proc_show);
0260     proc_create_single("legacy_device_resources", 0, proc_pnp,
0261             pnp_legacyres_proc_show);
0262     return 0;
0263 }
0264 
0265 void __exit pnpbios_proc_exit(void)
0266 {
0267     int i;
0268     char name[3];
0269 
0270     if (!proc_pnp)
0271         return;
0272 
0273     for (i = 0; i < 0xff; i++) {
0274         sprintf(name, "%02x", i);
0275         if (!pnpbios_dont_use_current_config)
0276             remove_proc_entry(name, proc_pnp);
0277         remove_proc_entry(name, proc_pnp_boot);
0278     }
0279     remove_proc_entry("legacy_device_resources", proc_pnp);
0280     remove_proc_entry("escd", proc_pnp);
0281     remove_proc_entry("escd_info", proc_pnp);
0282     remove_proc_entry("configuration_info", proc_pnp);
0283     remove_proc_entry("devices", proc_pnp);
0284     remove_proc_entry("boot", proc_pnp);
0285     remove_proc_entry("bus/pnp", NULL);
0286 }