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/pnp.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 "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 115 static 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 125 static 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 167 static 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 188 static 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; 216 out: 217 kfree(node); 218 return ret; 219 } 220 221 int 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 */ 257 int __init pnpbios_proc_init(void) 258 { 259 proc_pnp = proc_mkdir("bus/pnp", NULL); 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 277 void __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("bus/pnp", NULL); 298 } 299