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