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/kernel.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 I16(p) ((p)[0] + ((p)[1] << 8)) 39 #define I32(p) (I16(p) + (I16(p+2) << 16)) 40 41 /* 42 * Parse resource data for Logical Devices. 43 * 44 * This function exits as soon as it gets an error reading *ANY* 45 * Resource Data or ir reaches the end of Resource Data. In the first 46 * case the return value will be TRUE, FALSE otherwise. 47 */ 48 void 49 pnp_parse_resources(device_t dev, u_char *resources, int len) 50 { 51 device_t parent = device_get_parent(dev); 52 u_char tag, *resp, *resinfo; 53 int large_len, scanning = len; 54 u_int32_t id, compat_id; 55 struct isa_config logdev, alt; 56 struct isa_config *config; 57 int priority = 0; 58 int seenalt = 0; 59 char buf[100]; 60 61 id = isa_get_logicalid(dev); 62 bzero(&logdev, sizeof logdev); 63 bzero(&alt, sizeof alt); 64 config = &logdev; 65 resp = resources; 66 while (scanning > 0) { 67 tag = *resp++; 68 scanning--; 69 if (PNP_RES_TYPE(tag) == 0) { 70 /* Small resource */ 71 if (scanning < PNP_SRES_LEN(tag)) { 72 scanning = 0; 73 continue; 74 } 75 resinfo = resp; 76 resp += PNP_SRES_LEN(tag); 77 scanning -= PNP_SRES_LEN(tag);; 78 79 switch (PNP_SRES_NUM(tag)) { 80 case PNP_TAG_COMPAT_DEVICE: 81 /* 82 * Got a compatible device id 83 * resource. Should keep a list of 84 * compat ids in the device. 85 */ 86 bcopy(resinfo, &compat_id, 4); 87 isa_set_compatid(dev, compat_id); 88 break; 89 90 case PNP_TAG_IRQ_FORMAT: 91 if (bootverbose) { 92 printf("%s: adding irq mask %#04x\n", 93 pnp_eisaformat(id), 94 I16(resinfo)); 95 } 96 if (config->ic_nirq == ISA_NIRQ) { 97 device_printf(parent, "too many irqs"); 98 scanning = 0; 99 break; 100 } 101 config->ic_irqmask[config->ic_nirq] = 102 I16(resinfo); 103 config->ic_nirq++; 104 break; 105 106 case PNP_TAG_DMA_FORMAT: 107 if (bootverbose) { 108 printf("%s: adding dma mask %#02x\n", 109 pnp_eisaformat(id), 110 resinfo[0]); 111 } 112 if (config->ic_ndrq == ISA_NDRQ) { 113 device_printf(parent, "too many drqs"); 114 scanning = 0; 115 break; 116 } 117 config->ic_drqmask[config->ic_ndrq] = 118 resinfo[0]; 119 config->ic_ndrq++; 120 break; 121 122 case PNP_TAG_START_DEPENDANT: 123 if (bootverbose) { 124 printf("%s: start dependant\n", 125 pnp_eisaformat(id)); 126 } 127 if (config == &alt) { 128 ISA_ADD_CONFIG(parent, dev, 129 priority, config); 130 } else if (config != &logdev) { 131 device_printf(parent, "malformed\n"); 132 scanning = 0; 133 break; 134 } 135 /* 136 * If the priority is not specified, 137 * then use the default of 138 * 'acceptable' 139 */ 140 if (PNP_SRES_LEN(tag) > 0) 141 priority = resinfo[0]; 142 else 143 priority = 1; 144 alt = logdev; 145 config = &alt; 146 break; 147 148 case PNP_TAG_END_DEPENDANT: 149 if (bootverbose) { 150 printf("%s: end dependant\n", 151 pnp_eisaformat(id)); 152 } 153 ISA_ADD_CONFIG(parent, dev, priority, config); 154 config = &logdev; 155 seenalt = 1; 156 break; 157 158 case PNP_TAG_IO_RANGE: 159 if (bootverbose) { 160 printf("%s: adding io range " 161 "%#x-%#x, size=%#x, " 162 "align=%#x\n", 163 pnp_eisaformat(id), 164 I16(resinfo + 1), 165 I16(resinfo + 3) + resinfo[6]-1, 166 resinfo[6], 167 resinfo[5]); 168 } 169 if (config->ic_nport == ISA_NPORT) { 170 device_printf(parent, "too many ports"); 171 scanning = 0; 172 break; 173 } 174 config->ic_port[config->ic_nport].ir_start = 175 I16(resinfo + 1); 176 config->ic_port[config->ic_nport].ir_end = 177 I16(resinfo + 3) + resinfo[6] - 1; 178 config->ic_port[config->ic_nport].ir_size = 179 resinfo[6]; 180 if (resinfo[5] == 0) { 181 /* Make sure align is at least one */ 182 resinfo[5] = 1; 183 } 184 config->ic_port[config->ic_nport].ir_align = 185 resinfo[5]; 186 config->ic_nport++; 187 break; 188 189 case PNP_TAG_IO_FIXED: 190 if (bootverbose) { 191 printf("%s: adding io range " 192 "%#x-%#x, size=%#x, " 193 "align=%#x\n", 194 pnp_eisaformat(id), 195 I16(resinfo), 196 I16(resinfo) + resinfo[2] - 1, 197 resinfo[2], 198 1); 199 } 200 if (config->ic_nport == ISA_NPORT) { 201 device_printf(parent, "too many ports"); 202 scanning = 0; 203 break; 204 } 205 config->ic_port[config->ic_nport].ir_start = 206 I16(resinfo); 207 config->ic_port[config->ic_nport].ir_end = 208 I16(resinfo) + resinfo[2] - 1; 209 config->ic_port[config->ic_nport].ir_size 210 = resinfo[2]; 211 config->ic_port[config->ic_nport].ir_align = 1; 212 config->ic_nport++; 213 break; 214 215 case PNP_TAG_END: 216 if (bootverbose) { 217 printf("%s: start dependant\n", 218 pnp_eisaformat(id)); 219 } 220 scanning = 0; 221 break; 222 223 default: 224 /* Skip this resource */ 225 device_printf(parent, "unexpected tag %d\n", 226 PNP_SRES_NUM(tag)); 227 break; 228 } 229 } else { 230 /* Large resource */ 231 if (scanning < 2) { 232 scanning = 0; 233 continue; 234 } 235 large_len = I16(resp); 236 resp += 2; 237 scanning -= 2; 238 239 if (scanning < large_len) { 240 scanning = 0; 241 continue; 242 } 243 resinfo = resp; 244 resp += large_len; 245 scanning -= large_len; 246 247 switch (PNP_LRES_NUM(tag)) { 248 case PNP_TAG_ID_ANSI: 249 if (large_len > sizeof(buf) - 1) 250 large_len = sizeof(buf) - 1; 251 bcopy(resinfo, buf, large_len); 252 253 /* 254 * Trim trailing spaces. 255 */ 256 while (buf[large_len-1] == ' ') 257 large_len--; 258 buf[large_len] = '\0'; 259 device_set_desc_copy(dev, buf); 260 break; 261 262 case PNP_TAG_MEMORY_RANGE: 263 if (bootverbose) { 264 printf("%s: adding memory range " 265 "%#x-%#x, size=%#x, " 266 "align=%#x\n", 267 pnp_eisaformat(id), 268 I16(resinfo + 1)<<8, 269 (I16(resinfo + 3)<<8) 270 + I16(resinfo + 7) - 1, 271 I16(resinfo + 7), 272 I16(resinfo + 5)); 273 } 274 275 if (config->ic_nmem == ISA_NMEM) { 276 device_printf(parent, "too many memory ranges"); 277 scanning = 0; 278 break; 279 } 280 281 config->ic_mem[config->ic_nmem].ir_start = 282 I16(resinfo + 1)<<8; 283 config->ic_mem[config->ic_nmem].ir_end = 284 (I16(resinfo + 3)<<8) 285 + I16(resinfo + 7) - 1; 286 config->ic_mem[config->ic_nmem].ir_size = 287 I16(resinfo + 7); 288 config->ic_mem[config->ic_nmem].ir_align = 289 I16(resinfo + 5); 290 if (!config->ic_mem[config->ic_nmem].ir_align) 291 config->ic_mem[config->ic_nmem] 292 .ir_align = 0x10000; 293 config->ic_nmem++; 294 break; 295 296 case PNP_TAG_MEMORY32_RANGE: 297 if (bootverbose) { 298 printf("%s: adding memory range " 299 "%#x-%#x, size=%#x, " 300 "align=%#x\n", 301 pnp_eisaformat(id), 302 I32(resinfo + 1), 303 I32(resinfo + 5) 304 + I32(resinfo + 13) - 1, 305 I32(resinfo + 13), 306 I32(resinfo + 9)); 307 } 308 309 if (config->ic_nmem == ISA_NMEM) { 310 device_printf(parent, "too many memory ranges"); 311 scanning = 0; 312 break; 313 } 314 315 config->ic_mem[config->ic_nmem].ir_start = 316 I32(resinfo + 1); 317 config->ic_mem[config->ic_nmem].ir_end = 318 I32(resinfo + 5) 319 + I32(resinfo + 13) - 1; 320 config->ic_mem[config->ic_nmem].ir_size = 321 I32(resinfo + 13); 322 config->ic_mem[config->ic_nmem].ir_align = 323 I32(resinfo + 9); 324 config->ic_nmem++; 325 break; 326 327 case PNP_TAG_MEMORY32_FIXED: 328 if (I32(resinfo + 5) == 0) { 329 if (bootverbose) { 330 printf("%s: skipping empty range\n", 331 pnp_eisaformat(id)); 332 } 333 continue; 334 } 335 if (bootverbose) { 336 printf("%s: adding memory range " 337 "%#x-%#x, size=%#x\n", 338 pnp_eisaformat(id), 339 I32(resinfo + 1), 340 I32(resinfo + 1) 341 + I32(resinfo + 5) - 1, 342 I32(resinfo + 5)); 343 } 344 345 if (config->ic_nmem == ISA_NMEM) { 346 device_printf(parent, "too many memory ranges"); 347 scanning = 0; 348 break; 349 } 350 351 config->ic_mem[config->ic_nmem].ir_start = 352 I32(resinfo + 1); 353 config->ic_mem[config->ic_nmem].ir_end = 354 I32(resinfo + 1) 355 + I32(resinfo + 5) - 1; 356 config->ic_mem[config->ic_nmem].ir_size = 357 I32(resinfo + 5); 358 config->ic_mem[config->ic_nmem].ir_align = 1; 359 config->ic_nmem++; 360 break; 361 362 default: 363 /* Skip this resource */ 364 device_printf(parent, "unexpected tag %d\n", 365 PNP_SRES_NUM(tag)); 366 } 367 } 368 } 369 370 /* 371 * Some devices (e.g. network cards) don't have start 372 * dependant tags and only have a single configuration. If we 373 * finish parsing without seeing an end dependant tag, add the 374 * non-dependant configuration to the device. 375 */ 376 if (!seenalt) 377 ISA_ADD_CONFIG(parent, dev, 1, config); 378 } 379 380