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$ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/malloc.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 MAXDEP 8 39 40 #define I16(p) ((p)[0] + ((p)[1] << 8)) 41 #define I32(p) (I16(p) + (I16(p+2) << 16)) 42 43 /* 44 * Parse resource data for Logical Devices. 45 * 46 * This function exits as soon as it gets an error reading *ANY* 47 * Resource Data or it reaches the end of Resource Data. 48 */ 49 void 50 pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn) 51 { 52 device_t parent = device_get_parent(dev); 53 u_char tag, *resp, *resinfo; 54 int large_len, scanning = len; 55 u_int32_t id, compat_id; 56 struct isa_config *config; 57 int ncfgs = 1; 58 int priorities[1 + MAXDEP]; 59 struct isa_config *configs; 60 char buf[100]; 61 int i; 62 63 id = isa_get_logicalid(dev); 64 configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP), 65 M_DEVBUF, M_NOWAIT | M_ZERO); 66 if (configs == NULL) { 67 device_printf(dev, "No memory to parse PNP data\n"); 68 return; 69 } 70 config = &configs[0]; 71 priorities[0] = 0; 72 resp = resources; 73 while (scanning > 0) { 74 tag = *resp++; 75 scanning--; 76 if (PNP_RES_TYPE(tag) == 0) { 77 /* Small resource */ 78 if (scanning < PNP_SRES_LEN(tag)) { 79 scanning = 0; 80 continue; 81 } 82 resinfo = resp; 83 resp += PNP_SRES_LEN(tag); 84 scanning -= PNP_SRES_LEN(tag);; 85 86 switch (PNP_SRES_NUM(tag)) { 87 case PNP_TAG_COMPAT_DEVICE: 88 /* 89 * Got a compatible device id 90 * resource. Should keep a list of 91 * compat ids in the device. 92 */ 93 bcopy(resinfo, &compat_id, 4); 94 isa_set_compatid(dev, compat_id); 95 break; 96 97 case PNP_TAG_IRQ_FORMAT: 98 if (!I16(resinfo)) 99 break; 100 if (bootverbose) { 101 printf("%s: adding irq mask %#02x\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 pnp_check_quirks(vendor_id, 192 logical_id, 193 ldn, config); 194 break; 195 196 case PNP_TAG_IO_FIXED: 197 if (bootverbose) { 198 printf("%s: adding fixed io range " 199 "%#x-%#x, size=%#x, " 200 "align=%#x\n", 201 pnp_eisaformat(id), 202 I16(resinfo), 203 I16(resinfo) + resinfo[2] - 1, 204 resinfo[2], 205 1); 206 } 207 if (config->ic_nport == ISA_NPORT) { 208 device_printf(parent, "too many ports\n"); 209 scanning = 0; 210 break; 211 } 212 config->ic_port[config->ic_nport].ir_start = 213 I16(resinfo); 214 config->ic_port[config->ic_nport].ir_end = 215 I16(resinfo) + resinfo[2] - 1; 216 config->ic_port[config->ic_nport].ir_size 217 = resinfo[2]; 218 config->ic_port[config->ic_nport].ir_align = 1; 219 config->ic_nport++; 220 break; 221 222 case PNP_TAG_END: 223 if (bootverbose) { 224 printf("%s: end config\n", 225 pnp_eisaformat(id)); 226 } 227 scanning = 0; 228 break; 229 230 default: 231 /* Skip this resource */ 232 device_printf(parent, "unexpected small tag %d\n", 233 PNP_SRES_NUM(tag)); 234 break; 235 } 236 } else { 237 /* Large resource */ 238 if (scanning < 2) { 239 scanning = 0; 240 continue; 241 } 242 large_len = I16(resp); 243 resp += 2; 244 scanning -= 2; 245 246 if (scanning < large_len) { 247 scanning = 0; 248 continue; 249 } 250 resinfo = resp; 251 resp += large_len; 252 scanning -= large_len; 253 254 switch (PNP_LRES_NUM(tag)) { 255 case PNP_TAG_ID_ANSI: 256 if (large_len > sizeof(buf) - 1) 257 large_len = sizeof(buf) - 1; 258 bcopy(resinfo, buf, large_len); 259 260 /* 261 * Trim trailing spaces and garbage. 262 */ 263 while (large_len > 0 && buf[large_len - 1] <= ' ') 264 large_len--; 265 buf[large_len] = '\0'; 266 device_set_desc_copy(dev, buf); 267 break; 268 269 case PNP_TAG_MEMORY_RANGE: 270 if (bootverbose) { 271 int temp = I16(resinfo + 7) << 8; 272 273 printf("%s: adding memory range " 274 "%#x-%#x, size=%#x, " 275 "align=%#x\n", 276 pnp_eisaformat(id), 277 I16(resinfo + 1)<<8, 278 (I16(resinfo + 3)<<8) + temp - 1, 279 temp, 280 I16(resinfo + 5)); 281 } 282 283 if (config->ic_nmem == ISA_NMEM) { 284 device_printf(parent, "too many memory ranges\n"); 285 scanning = 0; 286 break; 287 } 288 289 config->ic_mem[config->ic_nmem].ir_start = 290 I16(resinfo + 1)<<8; 291 config->ic_mem[config->ic_nmem].ir_end = 292 (I16(resinfo + 3)<<8) 293 + (I16(resinfo + 7) << 8) - 1; 294 config->ic_mem[config->ic_nmem].ir_size = 295 I16(resinfo + 7) << 8; 296 config->ic_mem[config->ic_nmem].ir_align = 297 I16(resinfo + 5); 298 if (!config->ic_mem[config->ic_nmem].ir_align) 299 config->ic_mem[config->ic_nmem] 300 .ir_align = 0x10000; 301 config->ic_nmem++; 302 break; 303 304 case PNP_TAG_MEMORY32_RANGE: 305 if (I32(resinfo + 13) == 0) { 306 if (bootverbose) { 307 printf("%s: skipping empty range\n", 308 pnp_eisaformat(id)); 309 } 310 continue; 311 } 312 if (bootverbose) { 313 printf("%s: adding memory32 range " 314 "%#x-%#x, size=%#x, " 315 "align=%#x\n", 316 pnp_eisaformat(id), 317 I32(resinfo + 1), 318 I32(resinfo + 5) 319 + I32(resinfo + 13) - 1, 320 I32(resinfo + 13), 321 I32(resinfo + 9)); 322 } 323 324 if (config->ic_nmem == ISA_NMEM) { 325 device_printf(parent, "too many memory ranges\n"); 326 scanning = 0; 327 break; 328 } 329 330 config->ic_mem[config->ic_nmem].ir_start = 331 I32(resinfo + 1); 332 config->ic_mem[config->ic_nmem].ir_end = 333 I32(resinfo + 5) 334 + I32(resinfo + 13) - 1; 335 config->ic_mem[config->ic_nmem].ir_size = 336 I32(resinfo + 13); 337 config->ic_mem[config->ic_nmem].ir_align = 338 I32(resinfo + 9); 339 config->ic_nmem++; 340 break; 341 342 case PNP_TAG_MEMORY32_FIXED: 343 if (I32(resinfo + 5) == 0) { 344 if (bootverbose) { 345 printf("%s: skipping empty range\n", 346 pnp_eisaformat(id)); 347 } 348 continue; 349 } 350 if (bootverbose) { 351 printf("%s: adding fixed memory32 range " 352 "%#x-%#x, size=%#x\n", 353 pnp_eisaformat(id), 354 I32(resinfo + 1), 355 I32(resinfo + 1) 356 + I32(resinfo + 5) - 1, 357 I32(resinfo + 5)); 358 } 359 360 if (config->ic_nmem == ISA_NMEM) { 361 device_printf(parent, "too many memory ranges\n"); 362 scanning = 0; 363 break; 364 } 365 366 config->ic_mem[config->ic_nmem].ir_start = 367 I32(resinfo + 1); 368 config->ic_mem[config->ic_nmem].ir_end = 369 I32(resinfo + 1) 370 + I32(resinfo + 5) - 1; 371 config->ic_mem[config->ic_nmem].ir_size = 372 I32(resinfo + 5); 373 config->ic_mem[config->ic_nmem].ir_align = 1; 374 config->ic_nmem++; 375 break; 376 377 default: 378 /* Skip this resource */ 379 device_printf(parent, "unexpected large tag %d\n", 380 PNP_SRES_NUM(tag)); 381 } 382 } 383 } 384 if(ncfgs == 1) { 385 /* Single config without dependants */ 386 (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 387 free(configs, M_DEVBUF); 388 return; 389 } 390 /* Cycle through dependant configs merging primary details */ 391 for(i = 1; i < ncfgs; i++) { 392 int j; 393 config = &configs[i]; 394 for(j = 0; j < configs[0].ic_nmem; j++) { 395 if (config->ic_nmem == ISA_NMEM) { 396 device_printf(parent, "too many memory ranges\n"); 397 free(configs, M_DEVBUF); 398 return; 399 } 400 config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 401 config->ic_nmem++; 402 } 403 for(j = 0; j < configs[0].ic_nport; j++) { 404 if (config->ic_nport == ISA_NPORT) { 405 device_printf(parent, "too many port ranges\n"); 406 free(configs, M_DEVBUF); 407 return; 408 } 409 config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 410 config->ic_nport++; 411 } 412 for(j = 0; j < configs[0].ic_nirq; j++) { 413 if (config->ic_nirq == ISA_NIRQ) { 414 device_printf(parent, "too many irq ranges\n"); 415 free(configs, M_DEVBUF); 416 return; 417 } 418 config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 419 config->ic_nirq++; 420 } 421 for(j = 0; j < configs[0].ic_ndrq; j++) { 422 if (config->ic_ndrq == ISA_NDRQ) { 423 device_printf(parent, "too many drq ranges\n"); 424 free(configs, M_DEVBUF); 425 return; 426 } 427 config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 428 config->ic_ndrq++; 429 } 430 (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 431 } 432 free(configs, M_DEVBUF); 433 } 434