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