pnpparse.c revision 165654
155682Smarkm/*-
2233294Sstas * Copyright (c) 1999 Doug Rabson
3233294Sstas * All rights reserved.
4233294Sstas *
555682Smarkm * Redistribution and use in source and binary forms, with or without
6233294Sstas * modification, are permitted provided that the following conditions
7233294Sstas * are met:
8233294Sstas * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
10233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
13233294Sstas *
14233294Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24233294Sstas * SUCH DAMAGE.
25233294Sstas */
26233294Sstas
27233294Sstas#include <sys/cdefs.h>
28233294Sstas__FBSDID("$FreeBSD: head/sys/isa/pnpparse.c 165654 2006-12-30 11:55:47Z ceri $");
29233294Sstas
30233294Sstas#include <sys/param.h>
31233294Sstas#include <sys/systm.h>
3255682Smarkm#include <sys/malloc.h>
3355682Smarkm#include <sys/module.h>
3455682Smarkm#include <sys/bus.h>
3555682Smarkm
36233294Sstas#include <machine/stdarg.h>
3755682Smarkm
3855682Smarkm#include <isa/isavar.h>
3955682Smarkm#include <isa/pnpreg.h>
4055682Smarkm#include <isa/pnpvar.h>
4155682Smarkm
4255682Smarkm#define	MAXDEP	8
43178825Sdfr
4455682Smarkm#define I16(p)	((p)[0] + ((p)[1] << 8))
4555682Smarkm#define I32(p)	(I16(p) + (I16((p)+2) << 16))
46233294Sstas
47178825Sdfrvoid
48178825Sdfrpnp_printf(u_int32_t id, char *fmt, ...)
4955682Smarkm{
50178825Sdfr	va_list ap;
51233294Sstas
52178825Sdfr	va_start(ap, fmt);
53178825Sdfr	printf("%s: ", pnp_eisaformat(id));
54178825Sdfr	vprintf(fmt, ap);
55178825Sdfr	va_end(ap);
56178825Sdfr}
57178825Sdfr
58178825Sdfr/* parse a single descriptor */
59178825Sdfr
60178825Sdfrstatic int
61178825Sdfrpnp_parse_desc(device_t dev, u_char tag, u_char *res, int len,
62178825Sdfr	       struct isa_config *config, int ldn)
63233294Sstas{
64178825Sdfr	char buf[100];
65178825Sdfr	u_int32_t id;
6672445Sassar	u_int32_t compat_id;
6772445Sassar	int temp;
6855682Smarkm
6972445Sassar	id = isa_get_logicalid(dev);
7055682Smarkm
7172445Sassar	if (PNP_RES_TYPE(tag) == 0) {
7272445Sassar
7372445Sassar		/* Small resource */
7472445Sassar		switch (PNP_SRES_NUM(tag)) {
75178825Sdfr
76178825Sdfr		case PNP_TAG_VERSION:
77178825Sdfr		case PNP_TAG_VENDOR:
78178825Sdfr			/* these descriptors are quietly ignored */
79178825Sdfr			break;
80178825Sdfr
81178825Sdfr		case PNP_TAG_LOGICAL_DEVICE:
82178825Sdfr		case PNP_TAG_START_DEPENDANT:
83178825Sdfr		case PNP_TAG_END_DEPENDANT:
84178825Sdfr			if (bootverbose)
85178825Sdfr				pnp_printf(id, "unexpected small tag %d\n",
86178825Sdfr					   PNP_SRES_NUM(tag));
87178825Sdfr			/* shouldn't happen; quit now */
88178825Sdfr			return (1);
89178825Sdfr
90233294Sstas		case PNP_TAG_COMPAT_DEVICE:
91178825Sdfr			/*
92178825Sdfr			 * Got a compatible device id resource.
9372445Sassar			 * Should keep a list of compat ids in the device.
94178825Sdfr			 */
95178825Sdfr			bcopy(res, &compat_id, 4);
9672445Sassar			if (isa_get_compatid(dev) == 0)
9790926Snectar				isa_set_compatid(dev, compat_id);
9890926Snectar			break;
9990926Snectar
10072445Sassar		case PNP_TAG_IRQ_FORMAT:
10172445Sassar			if (config->ic_nirq == ISA_NIRQ) {
10272445Sassar				pnp_printf(id, "too many irqs\n");
10372445Sassar				return (1);
104178825Sdfr			}
105178825Sdfr			if (I16(res) == 0) {
106178825Sdfr				/* a null descriptor */
107178825Sdfr				config->ic_irqmask[config->ic_nirq] = 0;
108178825Sdfr				config->ic_nirq++;
109178825Sdfr				break;
110178825Sdfr			}
111178825Sdfr			if (bootverbose)
112178825Sdfr				pnp_printf(id, "adding irq mask %#02x\n",
113178825Sdfr					   I16(res));
114178825Sdfr			config->ic_irqmask[config->ic_nirq] = I16(res);
115178825Sdfr			config->ic_nirq++;
116178825Sdfr			break;
117178825Sdfr
118178825Sdfr		case PNP_TAG_DMA_FORMAT:
119178825Sdfr			if (config->ic_ndrq == ISA_NDRQ) {
120178825Sdfr				pnp_printf(id, "too many drqs\n");
121178825Sdfr				return (1);
122178825Sdfr			}
123178825Sdfr			if (res[0] == 0) {
124178825Sdfr				/* a null descriptor */
125178825Sdfr				config->ic_drqmask[config->ic_ndrq] = 0;
126178825Sdfr				config->ic_ndrq++;
127178825Sdfr				break;
12872445Sassar			}
12972445Sassar			if (bootverbose)
130178825Sdfr				pnp_printf(id, "adding dma mask %#02x\n",
131178825Sdfr					   res[0]);
132178825Sdfr			config->ic_drqmask[config->ic_ndrq] = res[0];
133178825Sdfr			config->ic_ndrq++;
13455682Smarkm			break;
13572445Sassar
13672445Sassar		case PNP_TAG_IO_RANGE:
137233294Sstas			if (config->ic_nport == ISA_NPORT) {
138178825Sdfr				pnp_printf(id, "too many ports\n");
139178825Sdfr				return (1);
140178825Sdfr			}
141178825Sdfr			if (res[6] == 0) {
14272445Sassar				/* a null descriptor */
143233294Sstas				config->ic_port[config->ic_nport].ir_start = 0;
144178825Sdfr				config->ic_port[config->ic_nport].ir_end = 0;
145178825Sdfr				config->ic_port[config->ic_nport].ir_size = 0;
146178825Sdfr				config->ic_port[config->ic_nport].ir_align = 0;
147178825Sdfr				config->ic_nport++;
14855682Smarkm				break;
149178825Sdfr			}
150178825Sdfr			if (bootverbose) {
151178825Sdfr				pnp_printf(id, "adding io range "
152233294Sstas					   "%#x-%#x, size=%#x, "
153233294Sstas					   "align=%#x\n",
154233294Sstas					   I16(res + 1),
155178825Sdfr					   I16(res + 3) + res[6]-1,
15672445Sassar					   res[6], res[5]);
15772445Sassar			}
158178825Sdfr			config->ic_port[config->ic_nport].ir_start =
159178825Sdfr			    I16(res + 1);
16072445Sassar			config->ic_port[config->ic_nport].ir_end =
161178825Sdfr			    I16(res + 3) + res[6] - 1;
162178825Sdfr			config->ic_port[config->ic_nport].ir_size = res[6];
163178825Sdfr			if (res[5] == 0) {
164178825Sdfr			    /* Make sure align is at least one */
16572445Sassar			    res[5] = 1;
16672445Sassar			}
167178825Sdfr			config->ic_port[config->ic_nport].ir_align = res[5];
168178825Sdfr			config->ic_nport++;
16972445Sassar			pnp_check_quirks(isa_get_vendorid(dev),
170178825Sdfr					 isa_get_logicalid(dev), ldn, config);
171178825Sdfr			break;
172178825Sdfr
173178825Sdfr		case PNP_TAG_IO_FIXED:
174178825Sdfr			if (config->ic_nport == ISA_NPORT) {
175178825Sdfr				pnp_printf(id, "too many ports\n");
176178825Sdfr				return (1);
177178825Sdfr			}
178178825Sdfr			if (res[2] == 0) {
179178825Sdfr				/* a null descriptor */
180178825Sdfr				config->ic_port[config->ic_nport].ir_start = 0;
18172445Sassar				config->ic_port[config->ic_nport].ir_end = 0;
18272445Sassar				config->ic_port[config->ic_nport].ir_size = 0;
183178825Sdfr				config->ic_port[config->ic_nport].ir_align = 0;
18472445Sassar				config->ic_nport++;
185233294Sstas				break;
186233294Sstas			}
18755682Smarkm			if (bootverbose) {
18872445Sassar				pnp_printf(id, "adding fixed io range "
18972445Sassar					   "%#x-%#x, size=%#x, "
190233294Sstas					   "align=%#x\n",
19172445Sassar					   I16(res),
19272445Sassar					   I16(res) + res[2] - 1,
193178825Sdfr					   res[2], 1);
19455682Smarkm			}
19572445Sassar			config->ic_port[config->ic_nport].ir_start = I16(res);
196233294Sstas			config->ic_port[config->ic_nport].ir_end =
197178825Sdfr			    I16(res) + res[2] - 1;
198233294Sstas			config->ic_port[config->ic_nport].ir_size = res[2];
199178825Sdfr			config->ic_port[config->ic_nport].ir_align = 1;
200233294Sstas			config->ic_nport++;
201178825Sdfr			break;
202178825Sdfr
203178825Sdfr		case PNP_TAG_END:
204178825Sdfr			if (bootverbose)
20572445Sassar				pnp_printf(id, "end config\n");
20655682Smarkm			return (1);
20772445Sassar
208178825Sdfr		default:
209178825Sdfr			/* Skip this resource */
21072445Sassar			pnp_printf(id, "unexpected small tag %d\n",
211178825Sdfr				      PNP_SRES_NUM(tag));
21272445Sassar			break;
21372445Sassar		}
21472445Sassar	} else {
21572445Sassar		/* Large resource */
21672445Sassar		switch (PNP_LRES_NUM(tag)) {
21772445Sassar
21872445Sassar		case PNP_TAG_ID_UNICODE:
21972445Sassar		case PNP_TAG_LARGE_VENDOR:
220233294Sstas			/* these descriptors are quietly ignored */
221233294Sstas			break;
222233294Sstas
223178825Sdfr		case PNP_TAG_ID_ANSI:
224178825Sdfr			if (len > sizeof(buf) - 1)
22572445Sassar				len = sizeof(buf) - 1;
226178825Sdfr			bcopy(res, buf, len);
227178825Sdfr
228178825Sdfr			/*
229178825Sdfr			 * Trim trailing spaces and garbage.
230178825Sdfr			 */
231178825Sdfr			while (len > 0 && buf[len - 1] <= ' ')
232178825Sdfr				len--;
233178825Sdfr			buf[len] = '\0';
234178825Sdfr			device_set_desc_copy(dev, buf);
235178825Sdfr			break;
236178825Sdfr
237178825Sdfr		case PNP_TAG_MEMORY_RANGE:
238178825Sdfr			if (config->ic_nmem == ISA_NMEM) {
239178825Sdfr				pnp_printf(id, "too many memory ranges\n");
240178825Sdfr				return (1);
241178825Sdfr			}
242178825Sdfr			if (I16(res + 7) == 0) {
243178825Sdfr				/* a null descriptor */
244178825Sdfr				config->ic_mem[config->ic_nmem].ir_start = 0;
245178825Sdfr				config->ic_mem[config->ic_nmem].ir_end = 0;
246178825Sdfr				config->ic_mem[config->ic_nmem].ir_size = 0;
247178825Sdfr				config->ic_mem[config->ic_nmem].ir_align = 0;
248233294Sstas				config->ic_nmem++;
249233294Sstas				break;
250178825Sdfr			}
251178825Sdfr			if (bootverbose) {
252233294Sstas				temp = I16(res + 7) << 8;
253178825Sdfr				pnp_printf(id, "adding memory range "
254178825Sdfr					   "%#x-%#x, size=%#x, "
255178825Sdfr					   "align=%#x\n",
256178825Sdfr					   I16(res + 1) << 8,
257178825Sdfr					   (I16(res + 3) << 8) + temp - 1,
258178825Sdfr					   temp, I16(res + 5));
259178825Sdfr			}
26072445Sassar			config->ic_mem[config->ic_nmem].ir_start =
26172445Sassar			    I16(res + 1) << 8;
26272445Sassar			config->ic_mem[config->ic_nmem].ir_end =
263178825Sdfr			    (I16(res + 3) << 8) + (I16(res + 7) << 8) - 1;
26455682Smarkm			config->ic_mem[config->ic_nmem].ir_size =
26555682Smarkm			    I16(res + 7) << 8;
26655682Smarkm			config->ic_mem[config->ic_nmem].ir_align = I16(res + 5);
26755682Smarkm			if (!config->ic_mem[config->ic_nmem].ir_align)
26855682Smarkm				config->ic_mem[config->ic_nmem].ir_align =
269178825Sdfr				    0x10000;
270233294Sstas			config->ic_nmem++;
271178825Sdfr			break;
272178825Sdfr
273178825Sdfr		case PNP_TAG_MEMORY32_RANGE:
274178825Sdfr			if (config->ic_nmem == ISA_NMEM) {
275233294Sstas				pnp_printf(id, "too many memory ranges\n");
276178825Sdfr				return (1);
277178825Sdfr			}
27855682Smarkm			if (I32(res + 13) == 0) {
27955682Smarkm				/* a null descriptor */
280				config->ic_mem[config->ic_nmem].ir_start = 0;
281				config->ic_mem[config->ic_nmem].ir_end = 0;
282				config->ic_mem[config->ic_nmem].ir_size = 0;
283				config->ic_mem[config->ic_nmem].ir_align = 0;
284				config->ic_nmem++;
285				break;
286			}
287			if (bootverbose) {
288				pnp_printf(id, "adding memory32 range "
289					   "%#x-%#x, size=%#x, "
290					   "align=%#x\n",
291					   I32(res + 1),
292					   I32(res + 5) + I32(res + 13) - 1,
293					   I32(res + 13), I32(res + 9));
294			}
295			config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
296			config->ic_mem[config->ic_nmem].ir_end =
297			    I32(res + 5) + I32(res + 13) - 1;
298			config->ic_mem[config->ic_nmem].ir_size = I32(res + 13);
299			config->ic_mem[config->ic_nmem].ir_align = I32(res + 9);
300			config->ic_nmem++;
301			break;
302
303		case PNP_TAG_MEMORY32_FIXED:
304			if (config->ic_nmem == ISA_NMEM) {
305				pnp_printf(id, "too many memory ranges\n");
306				return (1);
307			}
308			if (I32(res + 5) == 0) {
309				/* a null descriptor */
310				config->ic_mem[config->ic_nmem].ir_start = 0;
311				config->ic_mem[config->ic_nmem].ir_end = 0;
312				config->ic_mem[config->ic_nmem].ir_size = 0;
313				config->ic_mem[config->ic_nmem].ir_align = 0;
314				break;
315			}
316			if (bootverbose) {
317				pnp_printf(id, "adding fixed memory32 range "
318					   "%#x-%#x, size=%#x\n",
319					   I32(res + 1),
320					   I32(res + 1) + I32(res + 5) - 1,
321					   I32(res + 5));
322			}
323			config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
324			config->ic_mem[config->ic_nmem].ir_end =
325			    I32(res + 1) + I32(res + 5) - 1;
326			config->ic_mem[config->ic_nmem].ir_size = I32(res + 5);
327			config->ic_mem[config->ic_nmem].ir_align = 1;
328			config->ic_nmem++;
329			break;
330
331		default:
332			/* Skip this resource */
333			pnp_printf(id, "unexpected large tag %d\n",
334				   PNP_SRES_NUM(tag));
335			break;
336		}
337	}
338
339	return (0);
340}
341
342/*
343 * Parse a single "dependent" resource combination.
344 */
345
346u_char
347*pnp_parse_dependant(device_t dev, u_char *resources, int len,
348		     struct isa_config *config, int ldn)
349{
350
351	return pnp_scan_resources(dev, resources, len, config, ldn,
352				  pnp_parse_desc);
353}
354
355static void
356pnp_merge_resources(device_t dev, struct isa_config *from,
357		    struct isa_config *to)
358{
359	device_t parent;
360	int i;
361
362	parent = device_get_parent(dev);
363	for (i = 0; i < from->ic_nmem; i++) {
364		if (to->ic_nmem == ISA_NMEM) {
365			device_printf(parent, "too many memory ranges\n");
366			return;
367		}
368		to->ic_mem[to->ic_nmem] = from->ic_mem[i];
369		to->ic_nmem++;
370	}
371	for (i = 0; i < from->ic_nport; i++) {
372		if (to->ic_nport == ISA_NPORT) {
373			device_printf(parent, "too many port ranges\n");
374			return;
375		}
376		to->ic_port[to->ic_nport] = from->ic_port[i];
377		to->ic_nport++;
378	}
379	for (i = 0; i < from->ic_nirq; i++) {
380		if (to->ic_nirq == ISA_NIRQ) {
381			device_printf(parent, "too many irq ranges\n");
382			return;
383		}
384		to->ic_irqmask[to->ic_nirq] = from->ic_irqmask[i];
385		to->ic_nirq++;
386	}
387	for (i = 0; i < from->ic_ndrq; i++) {
388		if (to->ic_ndrq == ISA_NDRQ) {
389			device_printf(parent, "too many drq ranges\n");
390			return;
391		}
392		to->ic_drqmask[to->ic_ndrq] = from->ic_drqmask[i];
393		to->ic_ndrq++;
394	}
395}
396
397/*
398 * Parse resource data for Logical Devices, make a list of available
399 * resource configurations, and add them to the device.
400 *
401 * This function exits as soon as it gets an error reading *ANY*
402 * Resource Data or it reaches the end of Resource Data.
403 */
404
405void
406pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
407{
408	struct isa_config *configs;
409	struct isa_config *config;
410	device_t parent;
411	int priorities[1 + MAXDEP];
412	u_char *start;
413	u_char *p;
414	u_char tag;
415	u_int32_t id;
416	int ncfgs;
417	int l;
418	int i;
419
420	parent = device_get_parent(dev);
421	id = isa_get_logicalid(dev);
422
423	configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
424					      M_DEVBUF, M_NOWAIT | M_ZERO);
425	if (configs == NULL) {
426		device_printf(parent, "No memory to parse PNP data\n");
427		return;
428	}
429	config = &configs[0];
430	priorities[0] = 0;
431	ncfgs = 1;
432
433	p = resources;
434	start = NULL;
435	while (len > 0) {
436		tag = *p++;
437		len--;
438		if (PNP_RES_TYPE(tag) == 0) {
439			/* Small resource */
440			l = PNP_SRES_LEN(tag);
441			if (len < l) {
442				len = 0;
443				continue;
444			}
445			len -= l;
446
447			switch (PNP_SRES_NUM(tag)) {
448
449			case PNP_TAG_START_DEPENDANT:
450				if (start != NULL) {
451					/*
452					 * Copy the common resources first,
453					 * then parse the "dependent" resources.
454					 */
455					pnp_merge_resources(dev, &configs[0],
456							    config);
457					pnp_parse_dependant(dev, start,
458							    p - start - 1,
459							    config, ldn);
460				}
461				start = p + l;
462				if (ncfgs > MAXDEP) {
463					device_printf(parent, "too many dependent configs (%d)\n", MAXDEP);
464					len = 0;
465					break;
466				}
467				config = &configs[ncfgs];
468				/*
469				 * If the priority is not specified,
470				 * then use the default of 'acceptable'
471				 */
472				if (l > 0)
473					priorities[ncfgs] = p[0];
474				else
475					priorities[ncfgs] = 1;
476				if (bootverbose)
477					pnp_printf(id, "start dependent (%d)\n",
478						   priorities[ncfgs]);
479				ncfgs++;
480				break;
481
482			case PNP_TAG_END_DEPENDANT:
483				if (start == NULL) {
484					device_printf(parent,
485						      "malformed resources\n");
486					len = 0;
487					break;
488				}
489				/*
490				 * Copy the common resources first,
491				 * then parse the "dependent" resources.
492				 */
493				pnp_merge_resources(dev, &configs[0], config);
494				pnp_parse_dependant(dev, start, p - start - 1,
495						    config, ldn);
496				start = NULL;
497				if (bootverbose)
498					pnp_printf(id, "end dependent\n");
499				/*
500				 * Back to the common part; clear it
501				 * as its contents has already been copied
502				 * to each dependant.
503				 */
504				config = &configs[0];
505				bzero(config, sizeof(*config));
506				break;
507
508			case PNP_TAG_END:
509				if (start != NULL) {
510					device_printf(parent,
511						      "malformed resources\n");
512				}
513				len = 0;
514				break;
515
516			default:
517				if (start != NULL)
518					/* defer parsing a dependent section */
519					break;
520				if (pnp_parse_desc(dev, tag, p, l, config, ldn))
521					len = 0;
522				break;
523			}
524			p += l;
525		} else {
526			/* Large resource */
527			if (len < 2) {
528				len = 0;
529				break;
530			}
531			l = I16(p);
532			p += 2;
533			len -= 2;
534			if (len < l) {
535				len = 0;
536				break;
537			}
538			len -= l;
539			if (start == NULL &&
540			    pnp_parse_desc(dev, tag, p, l, config, ldn)) {
541				len = 0;
542				break;
543			}
544			p += l;
545		}
546	}
547
548	if (ncfgs == 1) {
549		/* Single config without dependants */
550		ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
551		free(configs, M_DEVBUF);
552		return;
553	}
554
555	for (i = 1; i < ncfgs; i++) {
556		/*
557		 * Merge the remaining part of the common resources,
558		 * if any. Strictly speaking, there shouldn't be common/main
559		 * resources after the END_DEPENDENT tag.
560		 */
561		pnp_merge_resources(dev, &configs[0], &configs[i]);
562		ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
563	}
564
565	free(configs, M_DEVBUF);
566}
567
568u_char
569*pnp_scan_resources(device_t dev, u_char *resources, int len,
570		    struct isa_config *config, int ldn, pnp_scan_cb *cb)
571{
572	u_char *p;
573	u_char tag;
574	int l;
575
576	p = resources;
577	while (len > 0) {
578		tag = *p++;
579		len--;
580		if (PNP_RES_TYPE(tag) == 0) {
581			/* small resource */
582			l = PNP_SRES_LEN(tag);
583			if (len < l)
584				break;
585			if ((*cb)(dev, tag, p, l, config, ldn))
586				return (p + l);
587			if (PNP_SRES_NUM(tag) == PNP_TAG_END)
588				return (p + l);
589		} else {
590			/* large resource */
591			if (len < 2)
592				break;
593			l = I16(p);
594			p += 2;
595			len -= 2;
596			if (len < l)
597				break;
598			if ((*cb)(dev, tag, p, l, config, ldn))
599				return (p + l);
600		}
601		p += l;
602		len -= l;
603	}
604	return NULL;
605}
606