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: src/sys/isa/pnpparse.c,v 1.2.2.3 2000/11/07 05:53:55 msmith Exp $ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/module.h> 34 #include <sys/bus.h> 35 #include <isa/isavar.h> 36 #include <isa/pnpreg.h> 37 #include <isa/pnpvar.h> 38 39 #define MAXDEP 8 40 41 #define I16(p) ((p)[0] + ((p)[1] << 8)) 42 #define I32(p) (I16(p) + (I16(p+2) << 16)) 43 44 /* 45 * Parse resource data for Logical Devices. 46 * 47 * This function exits as soon as it gets an error reading *ANY* 48 * Resource Data or it reaches the end of Resource Data. 49 */ 50 void 51 pnp_parse_resources(device_t dev, u_char *resources, int len) 52 { 53 device_t parent = device_get_parent(dev); 54 u_char tag, *resp, *resinfo; 55 int large_len, scanning = len; 56 u_int32_t id, compat_id; 57 struct isa_config *config; 58 int ncfgs = 1; 59 int priorities[1 + MAXDEP]; 60 struct isa_config *configs; 61 char buf[100]; 62 int i; 63 64 id = isa_get_logicalid(dev); 65 configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP), 66 M_DEVBUF, M_NOWAIT); 67 if (configs == NULL) { 68 device_printf(dev, "No memory to parse PNP data\n"); 69 return; 70 } 71 bzero(configs, sizeof(*configs) * (1 + MAXDEP)); 72 config = &configs[0]; 73 priorities[0] = 0; 74 resp = resources; 75 while (scanning > 0) { 76 tag = *resp++; 77 scanning--; 78 if (PNP_RES_TYPE(tag) == 0) { 79 /* Small resource */ 80 if (scanning < PNP_SRES_LEN(tag)) { 81 scanning = 0; 82 continue; 83 } 84 resinfo = resp; 85 resp += PNP_SRES_LEN(tag); 86 scanning -= PNP_SRES_LEN(tag);; 87 88 switch (PNP_SRES_NUM(tag)) { 89 case PNP_TAG_COMPAT_DEVICE: 90 /* 91 * Got a compatible device id 92 * resource. Should keep a list of 93 * compat ids in the device. 94 */ 95 bcopy(resinfo, &compat_id, 4); 96 isa_set_compatid(dev, compat_id); 97 break; 98 99 case PNP_TAG_IRQ_FORMAT: 100 if (bootverbose) { 101 printf("%s: adding irq mask %#04x\n", 102 pnp_eisaformat(id), 103 I16(resinfo)); 104 } 105 if (config->ic_nirq == ISA_NIRQ) { 106 device_printf(parent, "too many irqs\n"); 107 scanning = 0; 108 break; 109 } 110 config->ic_irqmask[config->ic_nirq] = 111 I16(resinfo); 112 config->ic_nirq++; 113 break; 114 115 case PNP_TAG_DMA_FORMAT: 116 if (bootverbose) { 117 printf("%s: adding dma mask %#02x\n", 118 pnp_eisaformat(id), 119 resinfo[0]); 120 } 121 if (config->ic_ndrq == ISA_NDRQ) { 122 device_printf(parent, "too many drqs\n"); 123 scanning = 0; 124 break; 125 } 126 config->ic_drqmask[config->ic_ndrq] = 127 resinfo[0]; 128 config->ic_ndrq++; 129 break; 130 131 case PNP_TAG_START_DEPENDANT: 132 if (bootverbose) { 133 printf("%s: start dependant\n", 134 pnp_eisaformat(id)); 135 } 136 if (ncfgs >= MAXDEP) { 137 device_printf(parent, "too many dependant configs (%d)\n", MAXDEP); 138 scanning = 0; 139 break; 140 } 141 config = &configs[ncfgs]; 142 /* 143 * If the priority is not specified, 144 * then use the default of 145 * 'acceptable' 146 */ 147 if (PNP_SRES_LEN(tag) > 0) 148 priorities[ncfgs] = resinfo[0]; 149 else 150 priorities[ncfgs] = 1; 151 ncfgs++; 152 break; 153 154 case PNP_TAG_END_DEPENDANT: 155 if (bootverbose) { 156 printf("%s: end dependant\n", 157 pnp_eisaformat(id)); 158 } 159 config = &configs[0]; /* back to main config */ 160 break; 161 162 case PNP_TAG_IO_RANGE: 163 if (bootverbose) { 164 printf("%s: adding io range " 165 "%#x-%#x, size=%#x, " 166 "align=%#x\n", 167 pnp_eisaformat(id), 168 I16(resinfo + 1), 169 I16(resinfo + 3) + resinfo[6]-1, 170 resinfo[6], 171 resinfo[5]); 172 } 173 if (config->ic_nport == ISA_NPORT) { 174 device_printf(parent, "too many ports\n"); 175 scanning = 0; 176 break; 177 } 178 config->ic_port[config->ic_nport].ir_start = 179 I16(resinfo + 1); 180 config->ic_port[config->ic_nport].ir_end = 181 I16(resinfo + 3) + resinfo[6] - 1; 182 config->ic_port[config->ic_nport].ir_size = 183 resinfo[6]; 184 if (resinfo[5] == 0) { 185 /* Make sure align is at least one */ 186 resinfo[5] = 1; 187 } 188 config->ic_port[config->ic_nport].ir_align = 189 resinfo[5]; 190 config->ic_nport++; 191 break; 192 193 case PNP_TAG_IO_FIXED: 194 if (bootverbose) { 195 printf("%s: adding fixed io range " 196 "%#x-%#x, size=%#x, " 197 "align=%#x\n", 198 pnp_eisaformat(id), 199 I16(resinfo), 200 I16(resinfo) + resinfo[2] - 1, 201 resinfo[2], 202 1); 203 } 204 if (config->ic_nport == ISA_NPORT) { 205 device_printf(parent, "too many ports\n"); 206 scanning = 0; 207 break; 208 } 209 config->ic_port[config->ic_nport].ir_start = 210 I16(resinfo); 211 config->ic_port[config->ic_nport].ir_end = 212 I16(resinfo) + resinfo[2] - 1; 213 config->ic_port[config->ic_nport].ir_size 214 = resinfo[2]; 215 config->ic_port[config->ic_nport].ir_align = 1; 216 config->ic_nport++; 217 break; 218 219 case PNP_TAG_END: 220 if (bootverbose) { 221 printf("%s: end config\n", 222 pnp_eisaformat(id)); 223 } 224 scanning = 0; 225 break; 226 227 default: 228 /* Skip this resource */ 229 device_printf(parent, "unexpected small tag %d\n", 230 PNP_SRES_NUM(tag)); 231 break; 232 } 233 } else { 234 /* Large resource */ 235 if (scanning < 2) { 236 scanning = 0; 237 continue; 238 } 239 large_len = I16(resp); 240 resp += 2; 241 scanning -= 2; 242 243 if (scanning < large_len) { 244 scanning = 0; 245 continue; 246 } 247 resinfo = resp; 248 resp += large_len; 249 scanning -= large_len; 250 251 switch (PNP_LRES_NUM(tag)) { 252 case PNP_TAG_ID_ANSI: 253 if (large_len > sizeof(buf) - 1) 254 large_len = sizeof(buf) - 1; 255 bcopy(resinfo, buf, large_len); 256 257 /* 258 * Trim trailing spaces and garbage. 259 */ 260 while (large_len > 0 && buf[large_len - 1] <= ' ') 261 large_len--; 262 buf[large_len] = '\0'; 263 device_set_desc_copy(dev, buf); 264 break; 265 266 case PNP_TAG_MEMORY_RANGE: 267 if (bootverbose) { 268 int temp = I16(resinfo + 7) << 8; 269 270 printf("%s: adding memory range " 271 "%#x-%#x, size=%#x, " 272 "align=%#x\n", 273 pnp_eisaformat(id), 274 I16(resinfo + 1)<<8, 275 (I16(resinfo + 3)<<8) + temp - 1, 276 temp, 277 I16(resinfo + 5)); 278 } 279 280 if (config->ic_nmem == ISA_NMEM) { 281 device_printf(parent, "too many memory ranges\n"); 282 scanning = 0; 283 break; 284 } 285 286 config->ic_mem[config->ic_nmem].ir_start = 287 I16(resinfo + 1)<<8; 288 config->ic_mem[config->ic_nmem].ir_end = 289 (I16(resinfo + 3)<<8) 290 + (I16(resinfo + 7) << 8) - 1; 291 config->ic_mem[config->ic_nmem].ir_size = 292 I16(resinfo + 7) << 8; 293 config->ic_mem[config->ic_nmem].ir_align = 294 I16(resinfo + 5); 295 if (!config->ic_mem[config->ic_nmem].ir_align) 296 config->ic_mem[config->ic_nmem] 297 .ir_align = 0x10000; 298 config->ic_nmem++; 299 break; 300 301 case PNP_TAG_MEMORY32_RANGE: 302 if (I32(resinfo + 13) == 0) { 303 if (bootverbose) { 304 printf("%s: skipping empty range\n", 305 pnp_eisaformat(id)); 306 } 307 continue; 308 } 309 if (bootverbose) { 310 printf("%s: adding memory32 range " 311 "%#x-%#x, size=%#x, " 312 "align=%#x\n", 313 pnp_eisaformat(id), 314 I32(resinfo + 1), 315 I32(resinfo + 5) 316 + I32(resinfo + 13) - 1, 317 I32(resinfo + 13), 318 I32(resinfo + 9)); 319 } 320 321 if (config->ic_nmem == ISA_NMEM) { 322 device_printf(parent, "too many memory ranges\n"); 323 scanning = 0; 324 break; 325 } 326 327 config->ic_mem[config->ic_nmem].ir_start = 328 I32(resinfo + 1); 329 config->ic_mem[config->ic_nmem].ir_end = 330 I32(resinfo + 5) 331 + I32(resinfo + 13) - 1; 332 config->ic_mem[config->ic_nmem].ir_size = 333 I32(resinfo + 13); 334 config->ic_mem[config->ic_nmem].ir_align = 335 I32(resinfo + 9); 336 config->ic_nmem++; 337 break; 338 339 case PNP_TAG_MEMORY32_FIXED: 340 if (I32(resinfo + 5) == 0) { 341 if (bootverbose) { 342 printf("%s: skipping empty range\n", 343 pnp_eisaformat(id)); 344 } 345 continue; 346 } 347 if (bootverbose) { 348 printf("%s: adding fixed memory32 range " 349 "%#x-%#x, size=%#x\n", 350 pnp_eisaformat(id), 351 I32(resinfo + 1), 352 I32(resinfo + 1) 353 + I32(resinfo + 5) - 1, 354 I32(resinfo + 5)); 355 } 356 357 if (config->ic_nmem == ISA_NMEM) { 358 device_printf(parent, "too many memory ranges\n"); 359 scanning = 0; 360 break; 361 } 362 363 config->ic_mem[config->ic_nmem].ir_start = 364 I32(resinfo + 1); 365 config->ic_mem[config->ic_nmem].ir_end = 366 I32(resinfo + 1) 367 + I32(resinfo + 5) - 1; 368 config->ic_mem[config->ic_nmem].ir_size = 369 I32(resinfo + 5); 370 config->ic_mem[config->ic_nmem].ir_align = 1; 371 config->ic_nmem++; 372 break; 373 374 default: 375 /* Skip this resource */ 376 device_printf(parent, "unexpected large tag %d\n", 377 PNP_SRES_NUM(tag)); 378 } 379 } 380 } 381 if(ncfgs == 1) { 382 /* Single config without dependants */ 383 (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 384 free(configs, M_DEVBUF); 385 return; 386 } 387 /* Cycle through dependant configs merging primary details */ 388 for(i = 1; i < ncfgs; i++) { 389 int j; 390 config = &configs[i]; 391 for(j = 0; j < configs[0].ic_nmem; j++) { 392 if (config->ic_nmem == ISA_NMEM) { 393 device_printf(parent, "too many memory ranges\n"); 394 free(configs, M_DEVBUF); 395 return; 396 } 397 config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 398 config->ic_nmem++; 399 } 400 for(j = 0; j < configs[0].ic_nport; j++) { 401 if (config->ic_nport == ISA_NPORT) { 402 device_printf(parent, "too many port ranges\n"); 403 free(configs, M_DEVBUF); 404 return; 405 } 406 config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 407 config->ic_nport++; 408 } 409 for(j = 0; j < configs[0].ic_nirq; j++) { 410 if (config->ic_nirq == ISA_NIRQ) { 411 device_printf(parent, "too many irq ranges\n"); 412 free(configs, M_DEVBUF); 413 return; 414 } 415 config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 416 config->ic_nirq++; 417 } 418 for(j = 0; j < configs[0].ic_ndrq; j++) { 419 if (config->ic_ndrq == ISA_NDRQ) { 420 device_printf(parent, "too many drq ranges\n"); 421 free(configs, M_DEVBUF); 422 return; 423 } 424 config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 425 config->ic_ndrq++; 426 } 427 (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 428 } 429 free(configs, M_DEVBUF); 430 } 431