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 (bootverbose) { 307 printf("%s: adding memory32 range " 308 "%#x-%#x, size=%#x, " 309 "align=%#x\n", 310 pnp_eisaformat(id), 311 I32(resinfo + 1), 312 I32(resinfo + 5) 313 + I32(resinfo + 13) - 1, 314 I32(resinfo + 13), 315 I32(resinfo + 9)); 316 } 317 318 if (config->ic_nmem == ISA_NMEM) { 319 device_printf(parent, "too many memory ranges\n"); 320 scanning = 0; 321 break; 322 } 323 324 config->ic_mem[config->ic_nmem].ir_start = 325 I32(resinfo + 1); 326 config->ic_mem[config->ic_nmem].ir_end = 327 I32(resinfo + 5) 328 + I32(resinfo + 13) - 1; 329 config->ic_mem[config->ic_nmem].ir_size = 330 I32(resinfo + 13); 331 config->ic_mem[config->ic_nmem].ir_align = 332 I32(resinfo + 9); 333 config->ic_nmem++; 334 break; 335 336 case PNP_TAG_MEMORY32_FIXED: 337 if (I32(resinfo + 5) == 0) { 338 if (bootverbose) { 339 printf("%s: skipping empty range\n", 340 pnp_eisaformat(id)); 341 } 342 continue; 343 } 344 if (bootverbose) { 345 printf("%s: adding fixed memory32 range " 346 "%#x-%#x, size=%#x\n", 347 pnp_eisaformat(id), 348 I32(resinfo + 1), 349 I32(resinfo + 1) 350 + I32(resinfo + 5) - 1, 351 I32(resinfo + 5)); 352 } 353 354 if (config->ic_nmem == ISA_NMEM) { 355 device_printf(parent, "too many memory ranges\n"); 356 scanning = 0; 357 break; 358 } 359 360 config->ic_mem[config->ic_nmem].ir_start = 361 I32(resinfo + 1); 362 config->ic_mem[config->ic_nmem].ir_end = 363 I32(resinfo + 1) 364 + I32(resinfo + 5) - 1; 365 config->ic_mem[config->ic_nmem].ir_size = 366 I32(resinfo + 5); 367 config->ic_mem[config->ic_nmem].ir_align = 1; 368 config->ic_nmem++; 369 break; 370 371 default: 372 /* Skip this resource */ 373 device_printf(parent, "unexpected large tag %d\n", 374 PNP_SRES_NUM(tag)); 375 } 376 } 377 } 378 if(ncfgs == 1) { 379 /* Single config without dependants */ 380 (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 381 free(configs, M_DEVBUF); 382 return; 383 } 384 /* Cycle through dependant configs merging primary details */ 385 for(i = 1; i < ncfgs; i++) { 386 int j; 387 config = &configs[i]; 388 for(j = 0; j < configs[0].ic_nmem; j++) { 389 if (config->ic_nmem == ISA_NMEM) { 390 device_printf(parent, "too many memory ranges\n"); 391 free(configs, M_DEVBUF); 392 return; 393 } 394 config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 395 config->ic_nmem++; 396 } 397 for(j = 0; j < configs[0].ic_nport; j++) { 398 if (config->ic_nport == ISA_NPORT) { 399 device_printf(parent, "too many port ranges\n"); 400 free(configs, M_DEVBUF); 401 return; 402 } 403 config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 404 config->ic_nport++; 405 } 406 for(j = 0; j < configs[0].ic_nirq; j++) { 407 if (config->ic_nirq == ISA_NIRQ) { 408 device_printf(parent, "too many irq ranges\n"); 409 free(configs, M_DEVBUF); 410 return; 411 } 412 config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 413 config->ic_nirq++; 414 } 415 for(j = 0; j < configs[0].ic_ndrq; j++) { 416 if (config->ic_ndrq == ISA_NDRQ) { 417 device_printf(parent, "too many drq ranges\n"); 418 free(configs, M_DEVBUF); 419 return; 420 } 421 config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 422 config->ic_ndrq++; 423 } 424 (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 425 } 426 free(configs, M_DEVBUF); 427 } 428