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 33 static struct proc_dir_entry *proc_pnp = NULL; 34 static struct proc_dir_entry *proc_pnp_boot = NULL; 35 36 static 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 50 static 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) 66 static 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 98 "PnPBIOS: proc_read_escd: ESCD size reported by BIOS read_escd call is too great\n"); 99 return -EFBIG; 100 } 101 102 escd_left_to_read = escd_size - pos; 103 if (escd_left_to_read < 0) 104 escd_left_to_read = 0; 105 if (escd_left_to_read == 0) 106 *eof = 1; 107 n = min(count, escd_left_to_read); 108 memcpy(buf, tmpbuf + pos, n); 109 kfree(tmpbuf); 110 *start = buf; 111 return n; 112 } 113 114 static int proc_read_legacyres(char *buf, char **start, off_t pos, 115 int count, int *eof, void *data) 116 { 117 /* Assume that the following won't overflow the buffer */ 118 if (pnp_bios_get_stat_res(buf)) 119 return -EIO; 120 121 return count; // FIXME: Return actual length 122 } 123 124 static int proc_read_devices(char *buf, char **start, off_t pos, 125 int count, int *eof, void *data) 126 { 127 struct pnp_bios_node *node; 128 u8 nodenum; 129 char *p = buf; 130 131 if (pos >= 0xff) 132 return 0; 133 134 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 135 if (!node) 136 return -ENOMEM; 137 138 for (nodenum = pos; nodenum < 0xff;) { 139 u8 thisnodenum = nodenum; 140 /* 26 = the number of characters per line sprintf'ed */ 141 if ((p - buf + 26) > count) 142 break; 143 if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node)) 144 break; 145 p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", 146 node->handle, node->eisa_id, 147 node->type_code[0], node->type_code[1], 148 node->type_code[2], node->flags); 149 if (nodenum <= thisnodenum) { 150 printk(KERN_ERR 151 "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", 152 "PnPBIOS: proc_read_devices:", 153 (unsigned int)nodenum, 154 (unsigned int)thisnodenum); 155 *eof = 1; 156 break; 157 } 158 } 159 kfree(node); 160 if (nodenum == 0xff) 161 *eof = 1; 162 *start = (char *)((off_t) nodenum - pos); 163 return p - buf; 164 } 165 166 static int proc_read_node(char *buf, char **start, off_t pos, 167 int count, int *eof, void *data) 168 { 169 struct pnp_bios_node *node; 170 int boot = (long)data >> 8; 171 u8 nodenum = (long)data; 172 int len; 173 174 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 175 if (!node) 176 return -ENOMEM; 177 if (pnp_bios_get_dev_node(&nodenum, boot, node)) { 178 kfree(node); 179 return -EIO; 180 } 181 len = node->size - sizeof(struct pnp_bios_node); 182 memcpy(buf, node->data, len); 183 kfree(node); 184 return len; 185 } 186 187 static int proc_write_node(struct file *file, const char __user * buf, 188 unsigned long count, void *data) 189 { 190 struct pnp_bios_node *node; 191 int boot = (long)data >> 8; 192 u8 nodenum = (long)data; 193 int ret = count; 194 195 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 196 if (!node) 197 return -ENOMEM; 198 if (pnp_bios_get_dev_node(&nodenum, boot, node)) { 199 ret = -EIO; 200 goto out; 201 } 202 if (count != node->size - sizeof(struct pnp_bios_node)) { 203 ret = -EINVAL; 204 goto out; 205 } 206 if (copy_from_user(node->data, buf, count)) { 207 ret = -EFAULT; 208 goto out; 209 } 210 if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) { 211 ret = -EINVAL; 212 goto out; 213 } 214 ret = count; 215 out: 216 kfree(node); 217 return ret; 218 } 219 220 int pnpbios_interface_attach_device(struct pnp_bios_node *node) 221 { 222 char name[3]; 223 struct proc_dir_entry *ent; 224 225 sprintf(name, "%02x", node->handle); 226 227 if (!proc_pnp) 228 return -EIO; 229 if (!pnpbios_dont_use_current_config) { 230 ent = create_proc_entry(name, 0, proc_pnp); 231 if (ent) { 232 ent->read_proc = proc_read_node; 233 ent->write_proc = proc_write_node; 234 ent->data = (void *)(long)(node->handle); 235 } 236 } 237 238 if (!proc_pnp_boot) 239 return -EIO; 240 ent = create_proc_entry(name, 0, proc_pnp_boot); 241 if (ent) { 242 ent->read_proc = proc_read_node; 243 ent->write_proc = proc_write_node; 244 ent->data = (void *)(long)(node->handle + 0x100); 245 return 0; 246 } 247 248 return -EIO; 249 } 250 251 /* 252 * When this is called, pnpbios functions are assumed to 253 * work and the pnpbios_dont_use_current_config flag 254 * should already have been set to the appropriate value 255 */ 256 int __init pnpbios_proc_init(void) 257 { 258 proc_pnp = proc_mkdir("pnp", proc_bus); 259 if (!proc_pnp) 260 return -EIO; 261 proc_pnp_boot = proc_mkdir("boot", proc_pnp); 262 if (!proc_pnp_boot) 263 return -EIO; 264 create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL); 265 create_proc_read_entry("configuration_info", 0, proc_pnp, 266 proc_read_pnpconfig, NULL); 267 create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo, 268 NULL); 269 create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL); 270 create_proc_read_entry("legacy_device_resources", 0, proc_pnp, 271 proc_read_legacyres, NULL); 272 273 return 0; 274 } 275 276 void __exit pnpbios_proc_exit(void) 277 { 278 int i; 279 char name[3]; 280 281 if (!proc_pnp) 282 return; 283 284 for (i = 0; i < 0xff; i++) { 285 sprintf(name, "%02x", i); 286 if (!pnpbios_dont_use_current_config) 287 remove_proc_entry(name, proc_pnp); 288 remove_proc_entry(name, proc_pnp_boot); 289 } 290 remove_proc_entry("legacy_device_resources", proc_pnp); 291 remove_proc_entry("escd", proc_pnp); 292 remove_proc_entry("escd_info", proc_pnp); 293 remove_proc_entry("configuration_info", proc_pnp); 294 remove_proc_entry("devices", proc_pnp); 295 remove_proc_entry("boot", proc_pnp); 296 remove_proc_entry("pnp", proc_bus); 297 } 298