pnpparse.c revision 58847
1/*-
2 * Copyright (c) 1999 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD: head/sys/isa/pnpparse.c 58847 2000-03-31 07:07:51Z dfr $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/module.h>
33#include <sys/bus.h>
34#include <isa/isavar.h>
35#include <isa/pnpreg.h>
36#include <isa/pnpvar.h>
37
38#define I16(p)	((p)[0] + ((p)[1] << 8))
39#define I32(p)	(I16(p) + (I16(p+2) << 16))
40
41/*
42 * Parse resource data for Logical Devices.
43 *
44 * This function exits as soon as it gets an error reading *ANY*
45 * Resource Data or ir reaches the end of Resource Data.  In the first
46 * case the return value will be TRUE, FALSE otherwise.
47 */
48void
49pnp_parse_resources(device_t dev, u_char *resources, int len)
50{
51	device_t parent = device_get_parent(dev);
52	u_char tag, *resp, *resinfo;
53	int large_len, scanning = len;
54	u_int32_t id, compat_id;
55	struct isa_config logdev, alt;
56	struct isa_config *config;
57	int priority = 0;
58	int seenalt = 0;
59	char buf[100];
60
61	id = isa_get_logicalid(dev);
62	bzero(&logdev, sizeof logdev);
63	bzero(&alt, sizeof alt);
64	config = &logdev;
65	resp = resources;
66	while (scanning > 0) {
67		tag = *resp++;
68		scanning--;
69		if (PNP_RES_TYPE(tag) == 0) {
70			/* Small resource */
71			if (scanning < PNP_SRES_LEN(tag)) {
72				scanning = 0;
73				continue;
74			}
75			resinfo = resp;
76			resp += PNP_SRES_LEN(tag);
77			scanning -= PNP_SRES_LEN(tag);;
78
79			switch (PNP_SRES_NUM(tag)) {
80			case PNP_TAG_COMPAT_DEVICE:
81				/*
82				 * Got a compatible device id
83				 * resource. Should keep a list of
84				 * compat ids in the device.
85				 */
86				bcopy(resinfo, &compat_id, 4);
87				isa_set_compatid(dev, compat_id);
88				break;
89
90			case PNP_TAG_IRQ_FORMAT:
91				if (bootverbose) {
92					printf("%s: adding irq mask %#04x\n",
93					       pnp_eisaformat(id),
94					       I16(resinfo));
95				}
96				if (config->ic_nirq == ISA_NIRQ) {
97					device_printf(parent, "too many irqs");
98					scanning = 0;
99					break;
100				}
101				config->ic_irqmask[config->ic_nirq] =
102					I16(resinfo);
103				config->ic_nirq++;
104				break;
105
106			case PNP_TAG_DMA_FORMAT:
107				if (bootverbose) {
108					printf("%s: adding dma mask %#02x\n",
109					       pnp_eisaformat(id),
110					       resinfo[0]);
111				}
112				if (config->ic_ndrq == ISA_NDRQ) {
113					device_printf(parent, "too many drqs");
114					scanning = 0;
115					break;
116				}
117				config->ic_drqmask[config->ic_ndrq] =
118					resinfo[0];
119				config->ic_ndrq++;
120				break;
121
122			case PNP_TAG_START_DEPENDANT:
123				if (bootverbose) {
124					printf("%s: start dependant\n",
125					       pnp_eisaformat(id));
126				}
127				if (config == &alt) {
128					ISA_ADD_CONFIG(parent, dev,
129						       priority, config);
130				} else if (config != &logdev) {
131					device_printf(parent, "malformed\n");
132					scanning = 0;
133					break;
134				}
135				/*
136				 * If the priority is not specified,
137				 * then use the default of
138				 * 'acceptable'
139				 */
140				if (PNP_SRES_LEN(tag) > 0)
141					priority = resinfo[0];
142				else
143					priority = 1;
144				alt = logdev;
145				config = &alt;
146				break;
147
148			case PNP_TAG_END_DEPENDANT:
149				if (bootverbose) {
150					printf("%s: end dependant\n",
151					       pnp_eisaformat(id));
152				}
153				ISA_ADD_CONFIG(parent, dev, priority, config);
154				config = &logdev;
155				seenalt = 1;
156				break;
157
158			case PNP_TAG_IO_RANGE:
159				if (bootverbose) {
160					printf("%s: adding io range "
161					       "%#x-%#x, size=%#x, "
162					       "align=%#x\n",
163					       pnp_eisaformat(id),
164					       I16(resinfo + 1),
165					       I16(resinfo + 3) + resinfo[6]-1,
166					       resinfo[6],
167					       resinfo[5]);
168				}
169				if (config->ic_nport == ISA_NPORT) {
170					device_printf(parent, "too many ports");
171					scanning = 0;
172					break;
173				}
174				config->ic_port[config->ic_nport].ir_start =
175					I16(resinfo + 1);
176				config->ic_port[config->ic_nport].ir_end =
177					I16(resinfo + 3) + resinfo[6] - 1;
178				config->ic_port[config->ic_nport].ir_size =
179					resinfo[6];
180				if (resinfo[5] == 0) {
181				    /* Make sure align is at least one */
182				    resinfo[5] = 1;
183				}
184				config->ic_port[config->ic_nport].ir_align =
185					resinfo[5];
186				config->ic_nport++;
187				break;
188
189			case PNP_TAG_IO_FIXED:
190				if (bootverbose) {
191					printf("%s: adding io range "
192					       "%#x-%#x, size=%#x, "
193					       "align=%#x\n",
194					       pnp_eisaformat(id),
195					       I16(resinfo),
196					       I16(resinfo) + resinfo[2] - 1,
197					       resinfo[2],
198					       1);
199				}
200				if (config->ic_nport == ISA_NPORT) {
201					device_printf(parent, "too many ports");
202					scanning = 0;
203					break;
204				}
205				config->ic_port[config->ic_nport].ir_start =
206					I16(resinfo);
207				config->ic_port[config->ic_nport].ir_end =
208					I16(resinfo) + resinfo[2] - 1;
209				config->ic_port[config->ic_nport].ir_size
210					= resinfo[2];
211				config->ic_port[config->ic_nport].ir_align = 1;
212				config->ic_nport++;
213				break;
214
215			case PNP_TAG_END:
216				if (bootverbose) {
217					printf("%s: start dependant\n",
218					       pnp_eisaformat(id));
219				}
220				scanning = 0;
221				break;
222
223			default:
224				/* Skip this resource */
225				device_printf(parent, "unexpected tag %d\n",
226					      PNP_SRES_NUM(tag));
227				break;
228			}
229		} else {
230			/* Large resource */
231			if (scanning < 2) {
232				scanning = 0;
233				continue;
234			}
235			large_len = I16(resp);
236			resp += 2;
237			scanning -= 2;
238
239			if (scanning < large_len) {
240				scanning = 0;
241				continue;
242			}
243			resinfo = resp;
244			resp += large_len;
245			scanning -= large_len;
246
247			switch (PNP_LRES_NUM(tag)) {
248			case PNP_TAG_ID_ANSI:
249				if (large_len > sizeof(buf) - 1)
250					large_len = sizeof(buf) - 1;
251				bcopy(resinfo, buf, large_len);
252
253				/*
254				 * Trim trailing spaces.
255				 */
256				while (buf[large_len-1] == ' ')
257					large_len--;
258				buf[large_len] = '\0';
259				device_set_desc_copy(dev, buf);
260				break;
261
262			case PNP_TAG_MEMORY_RANGE:
263				if (bootverbose) {
264					int temp = I16(resinfo + 7) << 8;
265
266					printf("%s: adding memory range "
267					       "%#x-%#x, size=%#x, "
268					       "align=%#x\n",
269					       pnp_eisaformat(id),
270					       I16(resinfo + 1)<<8,
271					       (I16(resinfo + 3)<<8) + temp - 1,
272					       temp,
273					       I16(resinfo + 5));
274				}
275
276				if (config->ic_nmem == ISA_NMEM) {
277					device_printf(parent, "too many memory ranges");
278					scanning = 0;
279					break;
280				}
281
282				config->ic_mem[config->ic_nmem].ir_start =
283					I16(resinfo + 1)<<8;
284				config->ic_mem[config->ic_nmem].ir_end =
285					(I16(resinfo + 3)<<8)
286					+ (I16(resinfo + 7) << 8) - 1;
287				config->ic_mem[config->ic_nmem].ir_size =
288					I16(resinfo + 7) << 8;
289				config->ic_mem[config->ic_nmem].ir_align =
290					I16(resinfo + 5);
291				if (!config->ic_mem[config->ic_nmem].ir_align)
292					config->ic_mem[config->ic_nmem]
293						.ir_align = 0x10000;
294				config->ic_nmem++;
295				break;
296
297			case PNP_TAG_MEMORY32_RANGE:
298				if (bootverbose) {
299					printf("%s: adding memory range "
300					       "%#x-%#x, size=%#x, "
301					       "align=%#x\n",
302					       pnp_eisaformat(id),
303					       I32(resinfo + 1),
304					       I32(resinfo + 5)
305					       + I32(resinfo + 13) - 1,
306					       I32(resinfo + 13),
307					       I32(resinfo + 9));
308				}
309
310				if (config->ic_nmem == ISA_NMEM) {
311					device_printf(parent, "too many memory ranges");
312					scanning = 0;
313					break;
314				}
315
316				config->ic_mem[config->ic_nmem].ir_start =
317					I32(resinfo + 1);
318				config->ic_mem[config->ic_nmem].ir_end =
319					I32(resinfo + 5)
320					+ I32(resinfo + 13) - 1;
321				config->ic_mem[config->ic_nmem].ir_size =
322					I32(resinfo + 13);
323				config->ic_mem[config->ic_nmem].ir_align =
324					I32(resinfo + 9);
325				config->ic_nmem++;
326				break;
327
328			case PNP_TAG_MEMORY32_FIXED:
329				if (I32(resinfo + 5) == 0) {
330					if (bootverbose) {
331						printf("%s: skipping empty range\n",
332						       pnp_eisaformat(id));
333					}
334					continue;
335				}
336				if (bootverbose) {
337					printf("%s: adding memory range "
338					       "%#x-%#x, size=%#x\n",
339					       pnp_eisaformat(id),
340					       I32(resinfo + 1),
341					       I32(resinfo + 1)
342					       + I32(resinfo + 5) - 1,
343					       I32(resinfo + 5));
344				}
345
346				if (config->ic_nmem == ISA_NMEM) {
347					device_printf(parent, "too many memory ranges");
348					scanning = 0;
349					break;
350				}
351
352				config->ic_mem[config->ic_nmem].ir_start =
353					I32(resinfo + 1);
354				config->ic_mem[config->ic_nmem].ir_end =
355					I32(resinfo + 1)
356					+ I32(resinfo + 5) - 1;
357				config->ic_mem[config->ic_nmem].ir_size =
358					I32(resinfo + 5);
359				config->ic_mem[config->ic_nmem].ir_align = 1;
360				config->ic_nmem++;
361				break;
362
363			default:
364				/* Skip this resource */
365				device_printf(parent, "unexpected tag %d\n",
366					      PNP_SRES_NUM(tag));
367			}
368		}
369	}
370
371	/*
372	 * Some devices (e.g. network cards) don't have start
373	 * dependant tags and only have a single configuration. If we
374	 * finish parsing without seeing an end dependant tag, add the
375	 * non-dependant configuration to the device.
376	 */
377	if (!seenalt)
378		ISA_ADD_CONFIG(parent, dev, 1, config);
379}
380
381