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