xref: /freebsd/sys/isa/pnpparse.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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$
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  */
48 void
49 pnp_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 					printf("%s: adding memory range "
265 					       "%#x-%#x, size=%#x, "
266 					       "align=%#x\n",
267 					       pnp_eisaformat(id),
268 					       I16(resinfo + 1)<<8,
269 					       (I16(resinfo + 3)<<8)
270 					       + I16(resinfo + 7) - 1,
271 					       I16(resinfo + 7),
272 					       I16(resinfo + 5));
273 				}
274 
275 				if (config->ic_nmem == ISA_NMEM) {
276 					device_printf(parent, "too many memory ranges");
277 					scanning = 0;
278 					break;
279 				}
280 
281 				config->ic_mem[config->ic_nmem].ir_start =
282 					I16(resinfo + 1)<<8;
283 				config->ic_mem[config->ic_nmem].ir_end =
284 					(I16(resinfo + 3)<<8)
285 					+ I16(resinfo + 7) - 1;
286 				config->ic_mem[config->ic_nmem].ir_size =
287 					I16(resinfo + 7);
288 				config->ic_mem[config->ic_nmem].ir_align =
289 					I16(resinfo + 5);
290 				if (!config->ic_mem[config->ic_nmem].ir_align)
291 					config->ic_mem[config->ic_nmem]
292 						.ir_align = 0x10000;
293 				config->ic_nmem++;
294 				break;
295 
296 			case PNP_TAG_MEMORY32_RANGE:
297 				if (bootverbose) {
298 					printf("%s: adding memory range "
299 					       "%#x-%#x, size=%#x, "
300 					       "align=%#x\n",
301 					       pnp_eisaformat(id),
302 					       I32(resinfo + 1),
303 					       I32(resinfo + 5)
304 					       + I32(resinfo + 13) - 1,
305 					       I32(resinfo + 13),
306 					       I32(resinfo + 9));
307 				}
308 
309 				if (config->ic_nmem == ISA_NMEM) {
310 					device_printf(parent, "too many memory ranges");
311 					scanning = 0;
312 					break;
313 				}
314 
315 				config->ic_mem[config->ic_nmem].ir_start =
316 					I32(resinfo + 1);
317 				config->ic_mem[config->ic_nmem].ir_end =
318 					I32(resinfo + 5)
319 					+ I32(resinfo + 13) - 1;
320 				config->ic_mem[config->ic_nmem].ir_size =
321 					I32(resinfo + 13);
322 				config->ic_mem[config->ic_nmem].ir_align =
323 					I32(resinfo + 9);
324 				config->ic_nmem++;
325 				break;
326 
327 			case PNP_TAG_MEMORY32_FIXED:
328 				if (I32(resinfo + 5) == 0) {
329 					if (bootverbose) {
330 						printf("%s: skipping empty range\n",
331 						       pnp_eisaformat(id));
332 					}
333 					continue;
334 				}
335 				if (bootverbose) {
336 					printf("%s: adding memory range "
337 					       "%#x-%#x, size=%#x\n",
338 					       pnp_eisaformat(id),
339 					       I32(resinfo + 1),
340 					       I32(resinfo + 1)
341 					       + I32(resinfo + 5) - 1,
342 					       I32(resinfo + 5));
343 				}
344 
345 				if (config->ic_nmem == ISA_NMEM) {
346 					device_printf(parent, "too many memory ranges");
347 					scanning = 0;
348 					break;
349 				}
350 
351 				config->ic_mem[config->ic_nmem].ir_start =
352 					I32(resinfo + 1);
353 				config->ic_mem[config->ic_nmem].ir_end =
354 					I32(resinfo + 1)
355 					+ I32(resinfo + 5) - 1;
356 				config->ic_mem[config->ic_nmem].ir_size =
357 					I32(resinfo + 5);
358 				config->ic_mem[config->ic_nmem].ir_align = 1;
359 				config->ic_nmem++;
360 				break;
361 
362 			default:
363 				/* Skip this resource */
364 				device_printf(parent, "unexpected tag %d\n",
365 					      PNP_SRES_NUM(tag));
366 			}
367 		}
368 	}
369 
370 	/*
371 	 * Some devices (e.g. network cards) don't have start
372 	 * dependant tags and only have a single configuration. If we
373 	 * finish parsing without seeing an end dependant tag, add the
374 	 * non-dependant configuration to the device.
375 	 */
376 	if (!seenalt)
377 		ISA_ADD_CONFIG(parent, dev, 1, config);
378 }
379 
380