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