isapnpres.c revision 1.6 1 /* $NetBSD: isapnpres.c,v 1.6 1997/04/10 07:02:58 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_devclass, dev->ipa_devclass,
127 sizeof(conf->ipa_devclass));
128
129 if (dev->ipa_child == NULL)
130 dev->ipa_child = conf;
131 else {
132 for (ipa = dev->ipa_child; ipa->ipa_sibling;
133 ipa = ipa->ipa_sibling)
134 continue;
135 ipa->ipa_sibling = conf;
136 }
137
138 return conf;
139 }
140
141
142 /* isapnp_merge():
143 * Merge the common device configurations to the subconfigurations
144 */
145 static void
146 isapnp_merge(c, d)
147 struct isapnp_attach_args *c;
148 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 */
172 static struct isapnp_attach_args *
173 isapnp_flatten(card)
174 struct isapnp_attach_args *card;
175 {
176 struct isapnp_attach_args *dev, *conf, *d, *c, *pa;
177
178 dev = card->ipa_child;
179 ISAPNP_FREE(card);
180
181 for (conf = c = NULL, d = dev; d; d = dev) {
182 dev = d->ipa_sibling;
183 if (d->ipa_child == NULL) {
184 /*
185 * No subconfigurations; all configuration info
186 * is in the device node.
187 */
188 d->ipa_sibling = NULL;
189 pa = d;
190 }
191 else {
192 /*
193 * Push down device configuration info to the
194 * subconfigurations
195 */
196 for (pa = d->ipa_child; pa; pa = pa->ipa_sibling)
197 isapnp_merge(pa, d);
198
199 pa = d->ipa_child;
200 ISAPNP_FREE(d);
201 }
202
203 if (c == NULL)
204 c = conf = pa;
205 else
206 c->ipa_sibling = pa;
207
208 while (c->ipa_sibling)
209 c = c->ipa_sibling;
210 }
211 return conf;
212 }
213
214
215 /* isapnp_process_tag():
216 * Process a resource tag
217 */
218 static int
219 isapnp_process_tag(tag, len, buf, card, dev, conf)
220 u_char tag, len, *buf;
221 struct isapnp_attach_args **card, **dev, **conf;
222 {
223 char str[64];
224 struct isapnp_region *r;
225 struct isapnp_pin *p;
226 struct isapnp_attach_args *pa;
227
228 #define COPY(a, b) strncpy((a), (b), sizeof(a)), (a)[sizeof(a) - 1] = '\0'
229
230 switch (tag) {
231 case ISAPNP_TAG_VERSION_NUM:
232 DPRINTF(("PnP version %d.%d, Vendor version %d.%d\n",
233 buf[0] >> 4, buf[0] & 0xf, buf[1] >> 4, buf[1] & 0xf));
234 return 0;
235
236 case ISAPNP_TAG_LOGICAL_DEV_ID:
237 (void) isapnp_id_to_vendor(str, buf);
238 DPRINTF(("Logical device id %s\n", str));
239
240 *dev = isapnp_newdev(*card);
241 COPY((*dev)->ipa_devlogic, str);
242 return 0;
243
244 case ISAPNP_TAG_COMPAT_DEV_ID:
245 (void) isapnp_id_to_vendor(str, buf);
246 DPRINTF(("Compatible device id %s\n", str));
247 return 0;
248
249 case ISAPNP_TAG_DEP_START:
250 if (len == 0)
251 buf[0] = ISAPNP_DEP_ACCEPTABLE;
252
253 if (*dev == NULL)
254 return -1;
255
256 *conf = isapnp_newconf(*dev);
257 (*conf)->ipa_pref = buf[0];
258 #ifdef DEBUG_ISAPNP
259 isapnp_print_dep_start(">>> Start dependent function ",
260 (*conf)->ipa_pref);
261 #endif
262 return 0;
263
264 case ISAPNP_TAG_DEP_END:
265 DPRINTF(("<<<End dependent functions\n"));
266 *conf = NULL;
267 return 0;
268
269 case ISAPNP_TAG_ANSI_IDENT_STRING:
270 buf[len] = '\0';
271 DPRINTF(("ANSI Ident: %s\n", buf));
272 if (*dev == NULL)
273 COPY((*card)->ipa_devident, buf);
274 else
275 COPY((*dev)->ipa_devclass, buf);
276 return 0;
277
278 case ISAPNP_TAG_END:
279 *dev = NULL;
280 return 0;
281
282 default:
283 /* Handled below */
284 break;
285 }
286
287
288 /*
289 * Decide which configuration we add the tag to
290 */
291 if (*conf)
292 pa = *conf;
293 else if (*dev)
294 pa = *dev;
295 else
296 /* error */
297 return -1;
298
299 switch (tag) {
300 case ISAPNP_TAG_IRQ_FORMAT:
301 if (len < 2)
302 break;
303
304 if (len != 3)
305 buf[2] = ISAPNP_IRQTYPE_EDGE_PLUS;
306
307 p = &pa->ipa_irq[pa->ipa_nirq++];
308 p->bits = buf[0] | (buf[1] << 8);
309 p->flags = buf[2];
310 #ifdef DEBUG_ISAPNP
311 isapnp_print_irq("", p);
312 #endif
313 break;
314
315 case ISAPNP_TAG_DMA_FORMAT:
316 if (buf[0] == 0)
317 break;
318
319 p = &pa->ipa_drq[pa->ipa_ndrq++];
320 p->bits = buf[0];
321 p->flags = buf[1];
322 #ifdef DEBUG_ISAPNP
323 isapnp_print_drq("", p);
324 #endif
325 break;
326
327
328 case ISAPNP_TAG_IO_PORT_DESC:
329 r = &pa->ipa_io[pa->ipa_nio++];
330 r->flags = buf[0];
331 r->minbase = (buf[2] << 8) | buf[1];
332 r->maxbase = (buf[4] << 8) | buf[3];
333 r->align = buf[5];
334 r->length = buf[6];
335 #ifdef DEBUG_ISAPNP
336 isapnp_print_io("", r);
337 #endif
338 break;
339
340 case ISAPNP_TAG_FIXED_IO_PORT_DESC:
341 r = &pa->ipa_io[pa->ipa_nio++];
342 r->flags = 0;
343 r->minbase = (buf[1] << 8) | buf[0];
344 r->maxbase = r->minbase;
345 r->align = 1;
346 r->length = buf[2];
347 #ifdef DEBUG_ISAPNP
348 isapnp_print_io("FIXED ", r);
349 #endif
350 break;
351
352 case ISAPNP_TAG_VENDOR_DEF:
353 DPRINTF(("Vendor defined (short)\n"));
354 break;
355
356 case ISAPNP_TAG_MEM_RANGE_DESC:
357 r = &pa->ipa_mem[pa->ipa_nmem++];
358 r->flags = buf[0];
359 r->minbase = (buf[2] << 16) | (buf[1] << 8);
360 r->maxbase = (buf[4] << 16) | (buf[3] << 8);
361 r->align = (buf[6] << 8) | buf[5];
362 r->length = (buf[8] << 16) | (buf[7] << 8);
363 #ifdef DEBUG_ISAPNP
364 isapnp_print_mem("", r);
365 #endif
366 break;
367
368
369 case ISAPNP_TAG_UNICODE_IDENT_STRING:
370 DPRINTF(("Unicode Ident\n"));
371 break;
372
373 case ISAPNP_TAG_VENDOR_DEFINED:
374 DPRINTF(("Vendor defined (long)\n"));
375 break;
376
377 case ISAPNP_TAG_MEM32_RANGE_DESC:
378 r = &pa->ipa_mem32[pa->ipa_nmem32++];
379 r->flags = buf[0];
380 r->minbase = (buf[4] << 24) | (buf[3] << 16) |
381 (buf[2] << 8) | buf[1];
382 r->maxbase = (buf[8] << 24) | (buf[7] << 16) |
383 (buf[6] << 8) | buf[5];
384 r->align = (buf[12] << 24) | (buf[11] << 16) |
385 (buf[10] << 8) | buf[9];
386 r->length = (buf[16] << 24) | (buf[15] << 16) |
387 (buf[14] << 8) | buf[13];
388 #ifdef DEBUG_ISAPNP
389 isapnp_print_mem("32-bit ", r);
390 #endif
391 break;
392
393 case ISAPNP_TAG_FIXED_MEM32_RANGE_DESC:
394 r = &pa->ipa_mem32[pa->ipa_nmem32++];
395 r->flags = buf[0];
396 r->minbase = (buf[4] << 24) | (buf[3] << 16) |
397 (buf[2] << 8) | buf[1];
398 r->maxbase = r->minbase;
399 r->align = 1;
400 r->length = (buf[8] << 24) | (buf[7] << 16) |
401 (buf[6] << 8) | buf[5];
402 #ifdef DEBUG_ISAPNP
403 isapnp_print_mem("FIXED 32-bit ", r);
404 #endif
405 break;
406
407 default:
408 #ifdef DEBUG_ISAPNP
409 {
410 int i;
411 printf("tag %.2x, len %d: ", tag, len);
412 for (i = 0; i < len; i++)
413 printf("%.2x ", buf[i]);
414 printf("\n");
415 }
416 #endif
417 break;
418 }
419 return 0;
420 }
421
422
423 /* isapnp_get_resource():
424 * Read the resources for card c
425 */
426 struct isapnp_attach_args *
427 isapnp_get_resource(sc, c)
428 struct isapnp_softc *sc;
429 int c;
430 {
431 u_char d, tag;
432 u_short len;
433 int i;
434 int warned = 0;
435 struct isapnp_attach_args *card, *dev = NULL, *conf = NULL;
436 u_char buf[ISAPNP_MAX_TAGSIZE], *p;
437
438 memset(buf, 0, sizeof(buf));
439
440 card = ISAPNP_MALLOC(sizeof(*card));
441 memset(card, 0, sizeof(*card));
442
443 #define NEXT_BYTE \
444 if (isapnp_wait_status(sc)) \
445 goto bad; \
446 d = isapnp_read_reg(sc, ISAPNP_RESOURCE_DATA)
447
448 for (i = 0; i < ISAPNP_SERIAL_SIZE; i++) {
449 NEXT_BYTE;
450
451 if (d != sc->sc_id[c][i] && i != ISAPNP_SERIAL_SIZE - 1) {
452 if (!warned) {
453 printf("%s: card %d violates PnP spec; byte %d\n",
454 sc->sc_dev.dv_xname, c + 1, i);
455 warned++;
456 }
457 if (i == 0) {
458 /*
459 * Magic! If this is the first byte, we
460 * assume that the tag data begins here.
461 */
462 goto parse;
463 }
464 }
465 }
466
467 do {
468 NEXT_BYTE;
469 parse:
470
471 if (d & ISAPNP_LARGE_TAG) {
472 tag = d;
473 NEXT_BYTE;
474 buf[0] = d;
475 NEXT_BYTE;
476 buf[1] = d;
477 len = (buf[1] << 8) | buf[0];
478 }
479 else {
480 tag = (d >> 3) & 0xf;
481 len = d & 0x7;
482 }
483
484 for (p = buf, i = 0; i < len; i++) {
485 NEXT_BYTE;
486 if (i < ISAPNP_MAX_TAGSIZE)
487 *p++ = d;
488 }
489
490 if (len >= ISAPNP_MAX_TAGSIZE) {
491 printf("%s: Maximum tag size exceeded, card %d\n",
492 sc->sc_dev.dv_xname, c + 1);
493 len = ISAPNP_MAX_TAGSIZE;
494 if (++warned == 10)
495 goto bad;
496 }
497
498 if (isapnp_process_tag(tag, len, buf, &card, &dev, &conf) == -1)
499 printf("%s: No current device for tag, card %d\n",
500 sc->sc_dev.dv_xname, c + 1);
501 }
502 while (tag != ISAPNP_TAG_END);
503 return isapnp_flatten(card);
504
505 bad:
506 for (card = isapnp_flatten(card); card; ) {
507 dev = card->ipa_sibling;
508 ISAPNP_FREE(card);
509 card = dev;
510 }
511 printf("%s: %s, card %d\n", sc->sc_dev.dv_xname,
512 warned >= 10 ? "Too many tag errors" : "Resource timeout", c + 1);
513 return NULL;
514 }
515