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) 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 (bootverbose) { 100 printf("%s: adding irq mask %#04x\n", 101 pnp_eisaformat(id), 102 I16(resinfo)); 103 } 104 if (config->ic_nirq == ISA_NIRQ) { 105 device_printf(parent, "too many irqs\n"); 106 scanning = 0; 107 break; 108 } 109 config->ic_irqmask[config->ic_nirq] = 110 I16(resinfo); 111 config->ic_nirq++; 112 break; 113 114 case PNP_TAG_DMA_FORMAT: 115 if (bootverbose) { 116 printf("%s: adding dma mask %#02x\n", 117 pnp_eisaformat(id), 118 resinfo[0]); 119 } 120 if (config->ic_ndrq == ISA_NDRQ) { 121 device_printf(parent, "too many drqs\n"); 122 scanning = 0; 123 break; 124 } 125 config->ic_drqmask[config->ic_ndrq] = 126 resinfo[0]; 127 config->ic_ndrq++; 128 break; 129 130 case PNP_TAG_START_DEPENDANT: 131 if (bootverbose) { 132 printf("%s: start dependant\n", 133 pnp_eisaformat(id)); 134 } 135 if (ncfgs > MAXDEP) { 136 device_printf(parent, "too many dependant configs (%d)\n", MAXDEP); 137 scanning = 0; 138 break; 139 } 140 config = &configs[ncfgs]; 141 /* 142 * If the priority is not specified, 143 * then use the default of 144 * 'acceptable' 145 */ 146 if (PNP_SRES_LEN(tag) > 0) 147 priorities[ncfgs] = resinfo[0]; 148 else 149 priorities[ncfgs] = 1; 150 ncfgs++; 151 break; 152 153 case PNP_TAG_END_DEPENDANT: 154 if (bootverbose) { 155 printf("%s: end dependant\n", 156 pnp_eisaformat(id)); 157 } 158 config = &configs[0]; /* back to main config */ 159 break; 160 161 case PNP_TAG_IO_RANGE: 162 if (bootverbose) { 163 printf("%s: adding io range " 164 "%#x-%#x, size=%#x, " 165 "align=%#x\n", 166 pnp_eisaformat(id), 167 I16(resinfo + 1), 168 I16(resinfo + 3) + resinfo[6]-1, 169 resinfo[6], 170 resinfo[5]); 171 } 172 if (config->ic_nport == ISA_NPORT) { 173 device_printf(parent, "too many ports\n"); 174 scanning = 0; 175 break; 176 } 177 config->ic_port[config->ic_nport].ir_start = 178 I16(resinfo + 1); 179 config->ic_port[config->ic_nport].ir_end = 180 I16(resinfo + 3) + resinfo[6] - 1; 181 config->ic_port[config->ic_nport].ir_size = 182 resinfo[6]; 183 if (resinfo[5] == 0) { 184 /* Make sure align is at least one */ 185 resinfo[5] = 1; 186 } 187 config->ic_port[config->ic_nport].ir_align = 188 resinfo[5]; 189 config->ic_nport++; 190 break; 191 192 case PNP_TAG_IO_FIXED: 193 if (bootverbose) { 194 printf("%s: adding fixed io range " 195 "%#x-%#x, size=%#x, " 196 "align=%#x\n", 197 pnp_eisaformat(id), 198 I16(resinfo), 199 I16(resinfo) + resinfo[2] - 1, 200 resinfo[2], 201 1); 202 } 203 if (config->ic_nport == ISA_NPORT) { 204 device_printf(parent, "too many ports\n"); 205 scanning = 0; 206 break; 207 } 208 config->ic_port[config->ic_nport].ir_start = 209 I16(resinfo); 210 config->ic_port[config->ic_nport].ir_end = 211 I16(resinfo) + resinfo[2] - 1; 212 config->ic_port[config->ic_nport].ir_size 213 = resinfo[2]; 214 config->ic_port[config->ic_nport].ir_align = 1; 215 config->ic_nport++; 216 break; 217 218 case PNP_TAG_END: 219 if (bootverbose) { 220 printf("%s: end config\n", 221 pnp_eisaformat(id)); 222 } 223 scanning = 0; 224 break; 225 226 default: 227 /* Skip this resource */ 228 device_printf(parent, "unexpected small tag %d\n", 229 PNP_SRES_NUM(tag)); 230 break; 231 } 232 } else { 233 /* Large resource */ 234 if (scanning < 2) { 235 scanning = 0; 236 continue; 237 } 238 large_len = I16(resp); 239 resp += 2; 240 scanning -= 2; 241 242 if (scanning < large_len) { 243 scanning = 0; 244 continue; 245 } 246 resinfo = resp; 247 resp += large_len; 248 scanning -= large_len; 249 250 switch (PNP_LRES_NUM(tag)) { 251 case PNP_TAG_ID_ANSI: 252 if (large_len > sizeof(buf) - 1) 253 large_len = sizeof(buf) - 1; 254 bcopy(resinfo, buf, large_len); 255 256 /* 257 * Trim trailing spaces and garbage. 258 */ 259 while (large_len > 0 && buf[large_len - 1] <= ' ') 260 large_len--; 261 buf[large_len] = '\0'; 262 device_set_desc_copy(dev, buf); 263 break; 264 265 case PNP_TAG_MEMORY_RANGE: 266 if (bootverbose) { 267 int temp = I16(resinfo + 7) << 8; 268 269 printf("%s: adding memory range " 270 "%#x-%#x, size=%#x, " 271 "align=%#x\n", 272 pnp_eisaformat(id), 273 I16(resinfo + 1)<<8, 274 (I16(resinfo + 3)<<8) + temp - 1, 275 temp, 276 I16(resinfo + 5)); 277 } 278 279 if (config->ic_nmem == ISA_NMEM) { 280 device_printf(parent, "too many memory ranges\n"); 281 scanning = 0; 282 break; 283 } 284 285 config->ic_mem[config->ic_nmem].ir_start = 286 I16(resinfo + 1)<<8; 287 config->ic_mem[config->ic_nmem].ir_end = 288 (I16(resinfo + 3)<<8) 289 + (I16(resinfo + 7) << 8) - 1; 290 config->ic_mem[config->ic_nmem].ir_size = 291 I16(resinfo + 7) << 8; 292 config->ic_mem[config->ic_nmem].ir_align = 293 I16(resinfo + 5); 294 if (!config->ic_mem[config->ic_nmem].ir_align) 295 config->ic_mem[config->ic_nmem] 296 .ir_align = 0x10000; 297 config->ic_nmem++; 298 break; 299 300 case PNP_TAG_MEMORY32_RANGE: 301 if (bootverbose) { 302 printf("%s: adding memory32 range " 303 "%#x-%#x, size=%#x, " 304 "align=%#x\n", 305 pnp_eisaformat(id), 306 I32(resinfo + 1), 307 I32(resinfo + 5) 308 + I32(resinfo + 13) - 1, 309 I32(resinfo + 13), 310 I32(resinfo + 9)); 311 } 312 313 if (config->ic_nmem == ISA_NMEM) { 314 device_printf(parent, "too many memory ranges\n"); 315 scanning = 0; 316 break; 317 } 318 319 config->ic_mem[config->ic_nmem].ir_start = 320 I32(resinfo + 1); 321 config->ic_mem[config->ic_nmem].ir_end = 322 I32(resinfo + 5) 323 + I32(resinfo + 13) - 1; 324 config->ic_mem[config->ic_nmem].ir_size = 325 I32(resinfo + 13); 326 config->ic_mem[config->ic_nmem].ir_align = 327 I32(resinfo + 9); 328 config->ic_nmem++; 329 break; 330 331 case PNP_TAG_MEMORY32_FIXED: 332 if (I32(resinfo + 5) == 0) { 333 if (bootverbose) { 334 printf("%s: skipping empty range\n", 335 pnp_eisaformat(id)); 336 } 337 continue; 338 } 339 if (bootverbose) { 340 printf("%s: adding fixed memory32 range " 341 "%#x-%#x, size=%#x\n", 342 pnp_eisaformat(id), 343 I32(resinfo + 1), 344 I32(resinfo + 1) 345 + I32(resinfo + 5) - 1, 346 I32(resinfo + 5)); 347 } 348 349 if (config->ic_nmem == ISA_NMEM) { 350 device_printf(parent, "too many memory ranges\n"); 351 scanning = 0; 352 break; 353 } 354 355 config->ic_mem[config->ic_nmem].ir_start = 356 I32(resinfo + 1); 357 config->ic_mem[config->ic_nmem].ir_end = 358 I32(resinfo + 1) 359 + I32(resinfo + 5) - 1; 360 config->ic_mem[config->ic_nmem].ir_size = 361 I32(resinfo + 5); 362 config->ic_mem[config->ic_nmem].ir_align = 1; 363 config->ic_nmem++; 364 break; 365 366 default: 367 /* Skip this resource */ 368 device_printf(parent, "unexpected large tag %d\n", 369 PNP_SRES_NUM(tag)); 370 } 371 } 372 } 373 if(ncfgs == 1) { 374 /* Single config without dependants */ 375 (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 376 free(configs, M_DEVBUF); 377 return; 378 } 379 /* Cycle through dependant configs merging primary details */ 380 for(i = 1; i < ncfgs; i++) { 381 int j; 382 config = &configs[i]; 383 for(j = 0; j < configs[0].ic_nmem; j++) { 384 if (config->ic_nmem == ISA_NMEM) { 385 device_printf(parent, "too many memory ranges\n"); 386 free(configs, M_DEVBUF); 387 return; 388 } 389 config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 390 config->ic_nmem++; 391 } 392 for(j = 0; j < configs[0].ic_nport; j++) { 393 if (config->ic_nport == ISA_NPORT) { 394 device_printf(parent, "too many port ranges\n"); 395 free(configs, M_DEVBUF); 396 return; 397 } 398 config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 399 config->ic_nport++; 400 } 401 for(j = 0; j < configs[0].ic_nirq; j++) { 402 if (config->ic_nirq == ISA_NIRQ) { 403 device_printf(parent, "too many irq ranges\n"); 404 free(configs, M_DEVBUF); 405 return; 406 } 407 config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 408 config->ic_nirq++; 409 } 410 for(j = 0; j < configs[0].ic_ndrq; j++) { 411 if (config->ic_ndrq == ISA_NDRQ) { 412 device_printf(parent, "too many drq ranges\n"); 413 free(configs, M_DEVBUF); 414 return; 415 } 416 config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 417 config->ic_ndrq++; 418 } 419 (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 420 } 421 free(configs, M_DEVBUF); 422 } 423