pnpparse.c revision 58847
1/*- 2 * Copyright (c) 1999 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/isa/pnpparse.c 58847 2000-03-31 07:07:51Z dfr $ 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/module.h> 33#include <sys/bus.h> 34#include <isa/isavar.h> 35#include <isa/pnpreg.h> 36#include <isa/pnpvar.h> 37 38#define I16(p) ((p)[0] + ((p)[1] << 8)) 39#define I32(p) (I16(p) + (I16(p+2) << 16)) 40 41/* 42 * Parse resource data for Logical Devices. 43 * 44 * This function exits as soon as it gets an error reading *ANY* 45 * Resource Data or ir reaches the end of Resource Data. In the first 46 * case the return value will be TRUE, FALSE otherwise. 47 */ 48void 49pnp_parse_resources(device_t dev, u_char *resources, int len) 50{ 51 device_t parent = device_get_parent(dev); 52 u_char tag, *resp, *resinfo; 53 int large_len, scanning = len; 54 u_int32_t id, compat_id; 55 struct isa_config logdev, alt; 56 struct isa_config *config; 57 int priority = 0; 58 int seenalt = 0; 59 char buf[100]; 60 61 id = isa_get_logicalid(dev); 62 bzero(&logdev, sizeof logdev); 63 bzero(&alt, sizeof alt); 64 config = &logdev; 65 resp = resources; 66 while (scanning > 0) { 67 tag = *resp++; 68 scanning--; 69 if (PNP_RES_TYPE(tag) == 0) { 70 /* Small resource */ 71 if (scanning < PNP_SRES_LEN(tag)) { 72 scanning = 0; 73 continue; 74 } 75 resinfo = resp; 76 resp += PNP_SRES_LEN(tag); 77 scanning -= PNP_SRES_LEN(tag);; 78 79 switch (PNP_SRES_NUM(tag)) { 80 case PNP_TAG_COMPAT_DEVICE: 81 /* 82 * Got a compatible device id 83 * resource. Should keep a list of 84 * compat ids in the device. 85 */ 86 bcopy(resinfo, &compat_id, 4); 87 isa_set_compatid(dev, compat_id); 88 break; 89 90 case PNP_TAG_IRQ_FORMAT: 91 if (bootverbose) { 92 printf("%s: adding irq mask %#04x\n", 93 pnp_eisaformat(id), 94 I16(resinfo)); 95 } 96 if (config->ic_nirq == ISA_NIRQ) { 97 device_printf(parent, "too many irqs"); 98 scanning = 0; 99 break; 100 } 101 config->ic_irqmask[config->ic_nirq] = 102 I16(resinfo); 103 config->ic_nirq++; 104 break; 105 106 case PNP_TAG_DMA_FORMAT: 107 if (bootverbose) { 108 printf("%s: adding dma mask %#02x\n", 109 pnp_eisaformat(id), 110 resinfo[0]); 111 } 112 if (config->ic_ndrq == ISA_NDRQ) { 113 device_printf(parent, "too many drqs"); 114 scanning = 0; 115 break; 116 } 117 config->ic_drqmask[config->ic_ndrq] = 118 resinfo[0]; 119 config->ic_ndrq++; 120 break; 121 122 case PNP_TAG_START_DEPENDANT: 123 if (bootverbose) { 124 printf("%s: start dependant\n", 125 pnp_eisaformat(id)); 126 } 127 if (config == &alt) { 128 ISA_ADD_CONFIG(parent, dev, 129 priority, config); 130 } else if (config != &logdev) { 131 device_printf(parent, "malformed\n"); 132 scanning = 0; 133 break; 134 } 135 /* 136 * If the priority is not specified, 137 * then use the default of 138 * 'acceptable' 139 */ 140 if (PNP_SRES_LEN(tag) > 0) 141 priority = resinfo[0]; 142 else 143 priority = 1; 144 alt = logdev; 145 config = &alt; 146 break; 147 148 case PNP_TAG_END_DEPENDANT: 149 if (bootverbose) { 150 printf("%s: end dependant\n", 151 pnp_eisaformat(id)); 152 } 153 ISA_ADD_CONFIG(parent, dev, priority, config); 154 config = &logdev; 155 seenalt = 1; 156 break; 157 158 case PNP_TAG_IO_RANGE: 159 if (bootverbose) { 160 printf("%s: adding io range " 161 "%#x-%#x, size=%#x, " 162 "align=%#x\n", 163 pnp_eisaformat(id), 164 I16(resinfo + 1), 165 I16(resinfo + 3) + resinfo[6]-1, 166 resinfo[6], 167 resinfo[5]); 168 } 169 if (config->ic_nport == ISA_NPORT) { 170 device_printf(parent, "too many ports"); 171 scanning = 0; 172 break; 173 } 174 config->ic_port[config->ic_nport].ir_start = 175 I16(resinfo + 1); 176 config->ic_port[config->ic_nport].ir_end = 177 I16(resinfo + 3) + resinfo[6] - 1; 178 config->ic_port[config->ic_nport].ir_size = 179 resinfo[6]; 180 if (resinfo[5] == 0) { 181 /* Make sure align is at least one */ 182 resinfo[5] = 1; 183 } 184 config->ic_port[config->ic_nport].ir_align = 185 resinfo[5]; 186 config->ic_nport++; 187 break; 188 189 case PNP_TAG_IO_FIXED: 190 if (bootverbose) { 191 printf("%s: adding io range " 192 "%#x-%#x, size=%#x, " 193 "align=%#x\n", 194 pnp_eisaformat(id), 195 I16(resinfo), 196 I16(resinfo) + resinfo[2] - 1, 197 resinfo[2], 198 1); 199 } 200 if (config->ic_nport == ISA_NPORT) { 201 device_printf(parent, "too many ports"); 202 scanning = 0; 203 break; 204 } 205 config->ic_port[config->ic_nport].ir_start = 206 I16(resinfo); 207 config->ic_port[config->ic_nport].ir_end = 208 I16(resinfo) + resinfo[2] - 1; 209 config->ic_port[config->ic_nport].ir_size 210 = resinfo[2]; 211 config->ic_port[config->ic_nport].ir_align = 1; 212 config->ic_nport++; 213 break; 214 215 case PNP_TAG_END: 216 if (bootverbose) { 217 printf("%s: start dependant\n", 218 pnp_eisaformat(id)); 219 } 220 scanning = 0; 221 break; 222 223 default: 224 /* Skip this resource */ 225 device_printf(parent, "unexpected tag %d\n", 226 PNP_SRES_NUM(tag)); 227 break; 228 } 229 } else { 230 /* Large resource */ 231 if (scanning < 2) { 232 scanning = 0; 233 continue; 234 } 235 large_len = I16(resp); 236 resp += 2; 237 scanning -= 2; 238 239 if (scanning < large_len) { 240 scanning = 0; 241 continue; 242 } 243 resinfo = resp; 244 resp += large_len; 245 scanning -= large_len; 246 247 switch (PNP_LRES_NUM(tag)) { 248 case PNP_TAG_ID_ANSI: 249 if (large_len > sizeof(buf) - 1) 250 large_len = sizeof(buf) - 1; 251 bcopy(resinfo, buf, large_len); 252 253 /* 254 * Trim trailing spaces. 255 */ 256 while (buf[large_len-1] == ' ') 257 large_len--; 258 buf[large_len] = '\0'; 259 device_set_desc_copy(dev, buf); 260 break; 261 262 case PNP_TAG_MEMORY_RANGE: 263 if (bootverbose) { 264 int temp = I16(resinfo + 7) << 8; 265 266 printf("%s: adding memory range " 267 "%#x-%#x, size=%#x, " 268 "align=%#x\n", 269 pnp_eisaformat(id), 270 I16(resinfo + 1)<<8, 271 (I16(resinfo + 3)<<8) + temp - 1, 272 temp, 273 I16(resinfo + 5)); 274 } 275 276 if (config->ic_nmem == ISA_NMEM) { 277 device_printf(parent, "too many memory ranges"); 278 scanning = 0; 279 break; 280 } 281 282 config->ic_mem[config->ic_nmem].ir_start = 283 I16(resinfo + 1)<<8; 284 config->ic_mem[config->ic_nmem].ir_end = 285 (I16(resinfo + 3)<<8) 286 + (I16(resinfo + 7) << 8) - 1; 287 config->ic_mem[config->ic_nmem].ir_size = 288 I16(resinfo + 7) << 8; 289 config->ic_mem[config->ic_nmem].ir_align = 290 I16(resinfo + 5); 291 if (!config->ic_mem[config->ic_nmem].ir_align) 292 config->ic_mem[config->ic_nmem] 293 .ir_align = 0x10000; 294 config->ic_nmem++; 295 break; 296 297 case PNP_TAG_MEMORY32_RANGE: 298 if (bootverbose) { 299 printf("%s: adding memory range " 300 "%#x-%#x, size=%#x, " 301 "align=%#x\n", 302 pnp_eisaformat(id), 303 I32(resinfo + 1), 304 I32(resinfo + 5) 305 + I32(resinfo + 13) - 1, 306 I32(resinfo + 13), 307 I32(resinfo + 9)); 308 } 309 310 if (config->ic_nmem == ISA_NMEM) { 311 device_printf(parent, "too many memory ranges"); 312 scanning = 0; 313 break; 314 } 315 316 config->ic_mem[config->ic_nmem].ir_start = 317 I32(resinfo + 1); 318 config->ic_mem[config->ic_nmem].ir_end = 319 I32(resinfo + 5) 320 + I32(resinfo + 13) - 1; 321 config->ic_mem[config->ic_nmem].ir_size = 322 I32(resinfo + 13); 323 config->ic_mem[config->ic_nmem].ir_align = 324 I32(resinfo + 9); 325 config->ic_nmem++; 326 break; 327 328 case PNP_TAG_MEMORY32_FIXED: 329 if (I32(resinfo + 5) == 0) { 330 if (bootverbose) { 331 printf("%s: skipping empty range\n", 332 pnp_eisaformat(id)); 333 } 334 continue; 335 } 336 if (bootverbose) { 337 printf("%s: adding memory range " 338 "%#x-%#x, size=%#x\n", 339 pnp_eisaformat(id), 340 I32(resinfo + 1), 341 I32(resinfo + 1) 342 + I32(resinfo + 5) - 1, 343 I32(resinfo + 5)); 344 } 345 346 if (config->ic_nmem == ISA_NMEM) { 347 device_printf(parent, "too many memory ranges"); 348 scanning = 0; 349 break; 350 } 351 352 config->ic_mem[config->ic_nmem].ir_start = 353 I32(resinfo + 1); 354 config->ic_mem[config->ic_nmem].ir_end = 355 I32(resinfo + 1) 356 + I32(resinfo + 5) - 1; 357 config->ic_mem[config->ic_nmem].ir_size = 358 I32(resinfo + 5); 359 config->ic_mem[config->ic_nmem].ir_align = 1; 360 config->ic_nmem++; 361 break; 362 363 default: 364 /* Skip this resource */ 365 device_printf(parent, "unexpected tag %d\n", 366 PNP_SRES_NUM(tag)); 367 } 368 } 369 } 370 371 /* 372 * Some devices (e.g. network cards) don't have start 373 * dependant tags and only have a single configuration. If we 374 * finish parsing without seeing an end dependant tag, add the 375 * non-dependant configuration to the device. 376 */ 377 if (!seenalt) 378 ISA_ADD_CONFIG(parent, dev, 1, config); 379} 380 381