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