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