1/* $NetBSD: isapnpres.c,v 1.22 2022/09/25 17:17:26 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Resource parser for Plug and Play cards. 34 */ 35 36#include <sys/cdefs.h> 37__KERNEL_RCSID(0, "$NetBSD: isapnpres.c,v 1.22 2022/09/25 17:17:26 thorpej Exp $"); 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/device.h> 42 43#include <sys/bus.h> 44 45#include <dev/isa/isavar.h> 46 47#include <dev/isapnp/isapnpreg.h> 48#include <dev/isapnp/isapnpvar.h> 49 50 51static int isapnp_wait_status(struct isapnp_softc *); 52static struct isapnp_attach_args * 53 isapnp_newdev(struct isapnp_attach_args *); 54static struct isapnp_attach_args * 55 isapnp_newconf(struct isapnp_attach_args *); 56static void isapnp_merge(struct isapnp_attach_args *, 57 const struct isapnp_attach_args *); 58static struct isapnp_attach_args * 59 isapnp_flatten(struct isapnp_attach_args *); 60static int isapnp_process_tag(u_char, u_char, u_char *, 61 struct isapnp_attach_args **, struct isapnp_attach_args **, 62 struct isapnp_attach_args **); 63 64 65/* isapnp_wait_status(): 66 * Wait for the next byte of resource data to become available 67 */ 68static int 69isapnp_wait_status(struct isapnp_softc *sc) 70{ 71 int i; 72 73 /* wait up to 1 ms for each resource byte */ 74 for (i = 0; i < 10; i++) { 75 if (isapnp_read_reg(sc, ISAPNP_STATUS) & 1) 76 return 0; 77 DELAY(100); 78 } 79 return 1; 80} 81 82 83/* isapnp_newdev(): 84 * Add a new logical device to the current card; expand the configuration 85 * resources of the current card if needed. 86 */ 87static struct isapnp_attach_args * 88isapnp_newdev(struct isapnp_attach_args *card) 89{ 90 struct isapnp_attach_args *ipa, *dev = ISAPNP_MALLOC(sizeof(*dev)); 91 92 memset(dev, 0, sizeof(*dev)); 93 94 dev->ipa_pref = ISAPNP_DEP_ACCEPTABLE; 95 memcpy(dev->ipa_devident, card->ipa_devident, 96 sizeof(card->ipa_devident)); 97 98 if (card->ipa_child == NULL) 99 card->ipa_child = dev; 100 else { 101 for (ipa = card->ipa_child; ipa->ipa_sibling != NULL; 102 ipa = ipa->ipa_sibling) 103 continue; 104 ipa->ipa_sibling = dev; 105 } 106 107 108 return dev; 109} 110 111 112/* isapnp_newconf(): 113 * Add a new alternate configuration to a logical device 114 */ 115static struct isapnp_attach_args * 116isapnp_newconf(struct isapnp_attach_args *dev) 117{ 118 struct isapnp_attach_args *ipa, *conf = ISAPNP_MALLOC(sizeof(*conf)); 119 120 memset(conf, 0, sizeof(*conf)); 121 122 memcpy(conf->ipa_devident, dev->ipa_devident, 123 sizeof(conf->ipa_devident)); 124 memcpy(conf->ipa_devlogic, dev->ipa_devlogic, 125 sizeof(conf->ipa_devlogic)); 126 memcpy(conf->ipa_devcompat, dev->ipa_devcompat, 127 sizeof(conf->ipa_devcompat)); 128 memcpy(conf->ipa_devclass, dev->ipa_devclass, 129 sizeof(conf->ipa_devclass)); 130 131 if (dev->ipa_child == NULL) 132 dev->ipa_child = conf; 133 else { 134 for (ipa = dev->ipa_child; ipa->ipa_sibling; 135 ipa = ipa->ipa_sibling) 136 continue; 137 ipa->ipa_sibling = conf; 138 } 139 140 return conf; 141} 142 143 144/* isapnp_merge(): 145 * Merge the common device configurations to the subconfigurations 146 */ 147static void 148isapnp_merge(struct isapnp_attach_args *c, const struct isapnp_attach_args *d) 149{ 150 int i; 151 152 for (i = 0; i < d->ipa_nio; i++) 153 c->ipa_io[c->ipa_nio++] = d->ipa_io[i]; 154 155 for (i = 0; i < d->ipa_nmem; i++) 156 c->ipa_mem[c->ipa_nmem++] = d->ipa_mem[i]; 157 158 for (i = 0; i < d->ipa_nmem32; i++) 159 c->ipa_mem32[c->ipa_nmem32++] = d->ipa_mem32[i]; 160 161 for (i = 0; i < d->ipa_nirq; i++) 162 c->ipa_irq[c->ipa_nirq++] = d->ipa_irq[i]; 163 164 for (i = 0; i < d->ipa_ndrq; i++) 165 c->ipa_drq[c->ipa_ndrq++] = d->ipa_drq[i]; 166} 167 168 169/* isapnp_flatten(): 170 * Flatten the tree to a list of config entries. 171 */ 172static struct isapnp_attach_args * 173isapnp_flatten(struct isapnp_attach_args *card) 174{ 175 struct isapnp_attach_args *dev, *conf, *d, *c, *pa; 176 177 dev = card->ipa_child; 178 ISAPNP_FREE(card); 179 180 for (conf = c = NULL, d = dev; d; d = dev) { 181 dev = d->ipa_sibling; 182 if (d->ipa_child == NULL) { 183 /* 184 * No subconfigurations; all configuration info 185 * is in the device node. 186 */ 187 d->ipa_sibling = NULL; 188 pa = d; 189 } 190 else { 191 /* 192 * Push down device configuration info to the 193 * subconfigurations 194 */ 195 for (pa = d->ipa_child; pa; pa = pa->ipa_sibling) 196 isapnp_merge(pa, d); 197 198 pa = d->ipa_child; 199 ISAPNP_FREE(d); 200 } 201 202 if (c == NULL) 203 c = conf = pa; 204 else 205 c->ipa_sibling = pa; 206 207 while (c->ipa_sibling) 208 c = c->ipa_sibling; 209 } 210 return conf; 211} 212 213 214/* isapnp_process_tag(): 215 * Process a resource tag 216 */ 217static int 218isapnp_process_tag(u_char tag, u_char len, u_char *buf, struct isapnp_attach_args **card, struct isapnp_attach_args **dev, struct isapnp_attach_args **conf) 219{ 220 char str[64]; 221 struct isapnp_region *r; 222 struct isapnp_pin *p; 223 struct isapnp_attach_args *pa; 224 225#define COPY(a, b) strncpy((a), (b), sizeof(a)), (a)[sizeof(a) - 1] = '\0' 226 227 switch (tag) { 228 case ISAPNP_TAG_VERSION_NUM: 229 DPRINTF(("PnP version %d.%d, Vendor version %d.%d\n", 230 buf[0] >> 4, buf[0] & 0xf, buf[1] >> 4, buf[1] & 0xf)); 231 return 0; 232 233 case ISAPNP_TAG_LOGICAL_DEV_ID: 234 (void) isapnp_id_to_vendor(str, buf); 235 DPRINTF(("Logical device id %s\n", str)); 236 237 *dev = isapnp_newdev(*card); 238 COPY((*dev)->ipa_devlogic, str); 239 return 0; 240 241 case ISAPNP_TAG_COMPAT_DEV_ID: 242 (void) isapnp_id_to_vendor(str, buf); 243 DPRINTF(("Compatible device id %s\n", str)); 244 245 if (*dev == NULL) 246 return -1; 247 248 if (*(*dev)->ipa_devcompat == '\0') 249 COPY((*dev)->ipa_devcompat, str); 250 return 0; 251 252 case ISAPNP_TAG_DEP_START: 253 if (len == 0) 254 buf[0] = ISAPNP_DEP_ACCEPTABLE; 255 256 if (*dev == NULL) 257 return -1; 258 259 *conf = isapnp_newconf(*dev); 260 (*conf)->ipa_pref = buf[0]; 261#ifdef DEBUG_ISAPNP 262 isapnp_print_dep_start(">>> Start dependent function ", 263 (*conf)->ipa_pref); 264#endif 265 return 0; 266 267 case ISAPNP_TAG_DEP_END: 268 DPRINTF(("<<<End dependent functions\n")); 269 *conf = NULL; 270 return 0; 271 272 case ISAPNP_TAG_ANSI_IDENT_STRING: 273 buf[len] = '\0'; 274 DPRINTF(("ANSI Ident: %s\n", buf)); 275 if (*dev == NULL) 276 COPY((*card)->ipa_devident, buf); 277 else 278 COPY((*dev)->ipa_devclass, buf); 279 return 0; 280 281 case ISAPNP_TAG_END: 282 *dev = NULL; 283 return 0; 284 285 default: 286 /* Handled below */ 287 break; 288 } 289 290 291 /* 292 * Decide which configuration we add the tag to 293 */ 294 if (*conf) 295 pa = *conf; 296 else if (*dev) 297 pa = *dev; 298 else 299 /* error */ 300 return -1; 301 302 switch (tag) { 303 case ISAPNP_TAG_IRQ_FORMAT: 304 if (len < 2) 305 break; 306 307 if (len != 3) 308 buf[2] = ISAPNP_IRQTYPE_EDGE_PLUS; 309 310 p = &pa->ipa_irq[pa->ipa_nirq++]; 311 p->bits = buf[0] | (buf[1] << 8); 312 p->flags = buf[2]; 313#ifdef DEBUG_ISAPNP 314 isapnp_print_irq("", p); 315#endif 316 break; 317 318 case ISAPNP_TAG_DMA_FORMAT: 319 if (buf[0] == 0) 320 break; 321 322 p = &pa->ipa_drq[pa->ipa_ndrq++]; 323 p->bits = buf[0]; 324 p->flags = buf[1]; 325#ifdef DEBUG_ISAPNP 326 isapnp_print_drq("", p); 327#endif 328 break; 329 330 331 case ISAPNP_TAG_IO_PORT_DESC: 332 r = &pa->ipa_io[pa->ipa_nio++]; 333 r->flags = buf[0]; 334 r->minbase = (buf[2] << 8) | buf[1]; 335 r->maxbase = (buf[4] << 8) | buf[3]; 336 r->align = buf[5]; 337 r->length = buf[6]; 338 if (r->length == 0) 339 pa->ipa_nio--; 340#ifdef DEBUG_ISAPNP 341 isapnp_print_io("", r); 342#endif 343 break; 344 345 case ISAPNP_TAG_FIXED_IO_PORT_DESC: 346 r = &pa->ipa_io[pa->ipa_nio++]; 347 r->flags = 0; 348 r->minbase = (buf[1] << 8) | buf[0]; 349 r->maxbase = r->minbase; 350 r->align = 1; 351 r->length = buf[2]; 352 if (r->length == 0) 353 pa->ipa_nio--; 354#ifdef DEBUG_ISAPNP 355 isapnp_print_io("FIXED ", r); 356#endif 357 break; 358 359 case ISAPNP_TAG_VENDOR_DEF: 360 DPRINTF(("Vendor defined (short)\n")); 361 break; 362 363 case ISAPNP_TAG_MEM_RANGE_DESC: 364 r = &pa->ipa_mem[pa->ipa_nmem++]; 365 r->flags = buf[0]; 366 r->minbase = (buf[2] << 16) | (buf[1] << 8); 367 r->maxbase = (buf[4] << 16) | (buf[3] << 8); 368 r->align = (buf[6] << 8) | buf[5]; 369 r->length = (buf[8] << 16) | (buf[7] << 8); 370 if (r->length == 0) 371 pa->ipa_nmem--; 372#ifdef DEBUG_ISAPNP 373 isapnp_print_mem("", r); 374#endif 375 break; 376 377 378 case ISAPNP_TAG_UNICODE_IDENT_STRING: 379 DPRINTF(("Unicode Ident\n")); 380 break; 381 382 case ISAPNP_TAG_VENDOR_DEFINED: 383 DPRINTF(("Vendor defined (long)\n")); 384 break; 385 386 case ISAPNP_TAG_MEM32_RANGE_DESC: 387 r = &pa->ipa_mem32[pa->ipa_nmem32++]; 388 r->flags = buf[0]; 389 r->minbase = (buf[4] << 24) | (buf[3] << 16) | 390 (buf[2] << 8) | buf[1]; 391 r->maxbase = (buf[8] << 24) | (buf[7] << 16) | 392 (buf[6] << 8) | buf[5]; 393 r->align = (buf[12] << 24) | (buf[11] << 16) | 394 (buf[10] << 8) | buf[9]; 395 r->length = (buf[16] << 24) | (buf[15] << 16) | 396 (buf[14] << 8) | buf[13]; 397 if (r->length == 0) 398 pa->ipa_nmem32--; 399#ifdef DEBUG_ISAPNP 400 isapnp_print_mem("32-bit ", r); 401#endif 402 break; 403 404 case ISAPNP_TAG_FIXED_MEM32_RANGE_DESC: 405 r = &pa->ipa_mem32[pa->ipa_nmem32++]; 406 r->flags = buf[0]; 407 r->minbase = (buf[4] << 24) | (buf[3] << 16) | 408 (buf[2] << 8) | buf[1]; 409 r->maxbase = r->minbase; 410 r->align = 1; 411 r->length = (buf[8] << 24) | (buf[7] << 16) | 412 (buf[6] << 8) | buf[5]; 413 if (r->length == 0) 414 pa->ipa_nmem32--; 415#ifdef DEBUG_ISAPNP 416 isapnp_print_mem("FIXED 32-bit ", r); 417#endif 418 break; 419 420 default: 421#ifdef DEBUG_ISAPNP 422 { 423 int i; 424 printf("tag %.2x, len %d: ", tag, len); 425 for (i = 0; i < len; i++) 426 printf("%.2x ", buf[i]); 427 printf("\n"); 428 } 429#endif 430 break; 431 } 432 return 0; 433} 434 435 436/* isapnp_get_resource(): 437 * Read the resources for card c 438 */ 439struct isapnp_attach_args * 440isapnp_get_resource(struct isapnp_softc *sc, int c) 441{ 442 u_char d, tag; 443 u_short len; 444 int i; 445 int warned = 0; 446 struct isapnp_attach_args *card, *dev = NULL, *conf = NULL; 447 u_char buf[ISAPNP_MAX_TAGSIZE], *p; 448 449 memset(buf, 0, sizeof(buf)); 450 451 card = ISAPNP_MALLOC(sizeof(*card)); 452 memset(card, 0, sizeof(*card)); 453 454#define NEXT_BYTE \ 455 if (isapnp_wait_status(sc)) \ 456 goto bad; \ 457 d = isapnp_read_reg(sc, ISAPNP_RESOURCE_DATA) 458 459 for (i = 0; i < ISAPNP_SERIAL_SIZE; i++) { 460 NEXT_BYTE; 461 462 if (d != sc->sc_id[c][i] && i != ISAPNP_SERIAL_SIZE - 1) { 463 if (!warned) { 464 aprint_error_dev(sc->sc_dev, 465 "card %d violates PnP spec; byte %d\n", 466 c + 1, i); 467 warned++; 468 } 469 if (i == 0) { 470 /* 471 * Magic! If this is the first byte, we 472 * assume that the tag data begins here. 473 */ 474 goto parse; 475 } 476 } 477 } 478 479 do { 480 NEXT_BYTE; 481parse: 482 483 if (d & ISAPNP_LARGE_TAG) { 484 tag = d; 485 NEXT_BYTE; 486 buf[0] = d; 487 NEXT_BYTE; 488 buf[1] = d; 489 len = (buf[1] << 8) | buf[0]; 490 } 491 else { 492 tag = (d >> 3) & 0xf; 493 len = d & 0x7; 494 } 495 496 for (p = buf, i = 0; i < len; i++) { 497 NEXT_BYTE; 498 if (i < ISAPNP_MAX_TAGSIZE) 499 *p++ = d; 500 } 501 502 if (len >= ISAPNP_MAX_TAGSIZE) { 503 aprint_error_dev(sc->sc_dev, 504 "Maximum tag size exceeded, card %d\n", 505 c + 1); 506 len = ISAPNP_MAX_TAGSIZE - 1; 507 if (++warned == 10) 508 goto bad; 509 } 510 511 if (isapnp_process_tag(tag, len, buf, &card, &dev, 512 &conf) == -1) { 513 aprint_error_dev(sc->sc_dev, 514 "No current device for tag, card %d\n", 515 c + 1); 516 if (++warned == 10) 517 goto bad; 518 } 519 } 520 while (tag != ISAPNP_TAG_END); 521 return isapnp_flatten(card); 522 523bad: 524 for (card = isapnp_flatten(card); card; ) { 525 dev = card->ipa_sibling; 526 ISAPNP_FREE(card); 527 card = dev; 528 } 529 aprint_normal_dev(sc->sc_dev, "%s, card %d\n", 530 warned >= 10 ? "Too many tag errors" : "Resource timeout", c + 1); 531 return NULL; 532} 533