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