xref: /freebsd/sys/isa/pnpparse.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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/malloc.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	MAXDEP	8
39 
40 #define I16(p)	((p)[0] + ((p)[1] << 8))
41 #define I32(p)	(I16(p) + (I16(p+2) << 16))
42 
43 /*
44  * Parse resource data for Logical Devices.
45  *
46  * This function exits as soon as it gets an error reading *ANY*
47  * Resource Data or it reaches the end of Resource Data.
48  */
49 void
50 pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn)
51 {
52 	device_t parent = device_get_parent(dev);
53 	u_char tag, *resp, *resinfo;
54 	int large_len, scanning = len;
55 	u_int32_t id, compat_id;
56 	struct isa_config *config;
57 	int ncfgs = 1;
58 	int priorities[1 + MAXDEP];
59 	struct isa_config *configs;
60 	char buf[100];
61 	int i;
62 
63 	id = isa_get_logicalid(dev);
64 	configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP),
65 						M_DEVBUF, M_NOWAIT | M_ZERO);
66 	if (configs == NULL) {
67 		device_printf(dev, "No memory to parse PNP data\n");
68 		return;
69 	}
70 	config = &configs[0];
71 	priorities[0] = 0;
72 	resp = resources;
73 	while (scanning > 0) {
74 		tag = *resp++;
75 		scanning--;
76 		if (PNP_RES_TYPE(tag) == 0) {
77 			/* Small resource */
78 			if (scanning < PNP_SRES_LEN(tag)) {
79 				scanning = 0;
80 				continue;
81 			}
82 			resinfo = resp;
83 			resp += PNP_SRES_LEN(tag);
84 			scanning -= PNP_SRES_LEN(tag);;
85 
86 			switch (PNP_SRES_NUM(tag)) {
87 			case PNP_TAG_COMPAT_DEVICE:
88 				/*
89 				 * Got a compatible device id
90 				 * resource. Should keep a list of
91 				 * compat ids in the device.
92 				 */
93 				bcopy(resinfo, &compat_id, 4);
94 				isa_set_compatid(dev, compat_id);
95 				break;
96 
97 			case PNP_TAG_IRQ_FORMAT:
98 				if (!I16(resinfo))
99 					break;
100 				if (bootverbose) {
101 					printf("%s: adding irq mask %#02x\n",
102 					       pnp_eisaformat(id),
103 					       I16(resinfo));
104 				}
105 				if (config->ic_nirq == ISA_NIRQ) {
106 					device_printf(parent, "too many irqs\n");
107 					scanning = 0;
108 					break;
109 				}
110 				config->ic_irqmask[config->ic_nirq] =
111 					I16(resinfo);
112 				config->ic_nirq++;
113 				break;
114 
115 			case PNP_TAG_DMA_FORMAT:
116 				if (bootverbose) {
117 					printf("%s: adding dma mask %#02x\n",
118 					       pnp_eisaformat(id),
119 					       resinfo[0]);
120 				}
121 				if (config->ic_ndrq == ISA_NDRQ) {
122 					device_printf(parent, "too many drqs\n");
123 					scanning = 0;
124 					break;
125 				}
126 				config->ic_drqmask[config->ic_ndrq] =
127 					resinfo[0];
128 				config->ic_ndrq++;
129 				break;
130 
131 			case PNP_TAG_START_DEPENDANT:
132 				if (bootverbose) {
133 					printf("%s: start dependant\n",
134 					       pnp_eisaformat(id));
135 				}
136 				if (ncfgs > MAXDEP) {
137 					device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
138 					scanning = 0;
139 					break;
140 				}
141 				config = &configs[ncfgs];
142 				/*
143 				 * If the priority is not specified,
144 				 * then use the default of
145 				 * 'acceptable'
146 				 */
147 				if (PNP_SRES_LEN(tag) > 0)
148 					priorities[ncfgs] = resinfo[0];
149 				else
150 					priorities[ncfgs] = 1;
151 				ncfgs++;
152 				break;
153 
154 			case PNP_TAG_END_DEPENDANT:
155 				if (bootverbose) {
156 					printf("%s: end dependant\n",
157 					       pnp_eisaformat(id));
158 				}
159 				config = &configs[0];	/* back to main config */
160 				break;
161 
162 			case PNP_TAG_IO_RANGE:
163 				if (bootverbose) {
164 					printf("%s: adding io range "
165 					       "%#x-%#x, size=%#x, "
166 					       "align=%#x\n",
167 					       pnp_eisaformat(id),
168 					       I16(resinfo + 1),
169 					       I16(resinfo + 3) + resinfo[6]-1,
170 					       resinfo[6],
171 					       resinfo[5]);
172 				}
173 				if (config->ic_nport == ISA_NPORT) {
174 					device_printf(parent, "too many ports\n");
175 					scanning = 0;
176 					break;
177 				}
178 				config->ic_port[config->ic_nport].ir_start =
179 					I16(resinfo + 1);
180 				config->ic_port[config->ic_nport].ir_end =
181 					I16(resinfo + 3) + resinfo[6] - 1;
182 				config->ic_port[config->ic_nport].ir_size =
183 					resinfo[6];
184 				if (resinfo[5] == 0) {
185 				    /* Make sure align is at least one */
186 				    resinfo[5] = 1;
187 				}
188 				config->ic_port[config->ic_nport].ir_align =
189 					resinfo[5];
190 				config->ic_nport++;
191 				pnp_check_quirks(vendor_id,
192 						 logical_id,
193 						 ldn, config);
194 				break;
195 
196 			case PNP_TAG_IO_FIXED:
197 				if (bootverbose) {
198 					printf("%s: adding fixed io range "
199 					       "%#x-%#x, size=%#x, "
200 					       "align=%#x\n",
201 					       pnp_eisaformat(id),
202 					       I16(resinfo),
203 					       I16(resinfo) + resinfo[2] - 1,
204 					       resinfo[2],
205 					       1);
206 				}
207 				if (config->ic_nport == ISA_NPORT) {
208 					device_printf(parent, "too many ports\n");
209 					scanning = 0;
210 					break;
211 				}
212 				config->ic_port[config->ic_nport].ir_start =
213 					I16(resinfo);
214 				config->ic_port[config->ic_nport].ir_end =
215 					I16(resinfo) + resinfo[2] - 1;
216 				config->ic_port[config->ic_nport].ir_size
217 					= resinfo[2];
218 				config->ic_port[config->ic_nport].ir_align = 1;
219 				config->ic_nport++;
220 				break;
221 
222 			case PNP_TAG_END:
223 				if (bootverbose) {
224 					printf("%s: end config\n",
225 					       pnp_eisaformat(id));
226 				}
227 				scanning = 0;
228 				break;
229 
230 			default:
231 				/* Skip this resource */
232 				device_printf(parent, "unexpected small tag %d\n",
233 					      PNP_SRES_NUM(tag));
234 				break;
235 			}
236 		} else {
237 			/* Large resource */
238 			if (scanning < 2) {
239 				scanning = 0;
240 				continue;
241 			}
242 			large_len = I16(resp);
243 			resp += 2;
244 			scanning -= 2;
245 
246 			if (scanning < large_len) {
247 				scanning = 0;
248 				continue;
249 			}
250 			resinfo = resp;
251 			resp += large_len;
252 			scanning -= large_len;
253 
254 			switch (PNP_LRES_NUM(tag)) {
255 			case PNP_TAG_ID_ANSI:
256 				if (large_len > sizeof(buf) - 1)
257 					large_len = sizeof(buf) - 1;
258 				bcopy(resinfo, buf, large_len);
259 
260 				/*
261 				 * Trim trailing spaces and garbage.
262 				 */
263 				while (large_len > 0 && buf[large_len - 1] <= ' ')
264 					large_len--;
265 				buf[large_len] = '\0';
266 				device_set_desc_copy(dev, buf);
267 				break;
268 
269 			case PNP_TAG_MEMORY_RANGE:
270 				if (bootverbose) {
271 					int temp = I16(resinfo + 7) << 8;
272 
273 					printf("%s: adding memory range "
274 					       "%#x-%#x, size=%#x, "
275 					       "align=%#x\n",
276 					       pnp_eisaformat(id),
277 					       I16(resinfo + 1)<<8,
278 					       (I16(resinfo + 3)<<8) + temp - 1,
279 					       temp,
280 					       I16(resinfo + 5));
281 				}
282 
283 				if (config->ic_nmem == ISA_NMEM) {
284 					device_printf(parent, "too many memory ranges\n");
285 					scanning = 0;
286 					break;
287 				}
288 
289 				config->ic_mem[config->ic_nmem].ir_start =
290 					I16(resinfo + 1)<<8;
291 				config->ic_mem[config->ic_nmem].ir_end =
292 					(I16(resinfo + 3)<<8)
293 					+ (I16(resinfo + 7) << 8) - 1;
294 				config->ic_mem[config->ic_nmem].ir_size =
295 					I16(resinfo + 7) << 8;
296 				config->ic_mem[config->ic_nmem].ir_align =
297 					I16(resinfo + 5);
298 				if (!config->ic_mem[config->ic_nmem].ir_align)
299 					config->ic_mem[config->ic_nmem]
300 						.ir_align = 0x10000;
301 				config->ic_nmem++;
302 				break;
303 
304 			case PNP_TAG_MEMORY32_RANGE:
305 				if (I32(resinfo + 13) == 0) {
306 					if (bootverbose) {
307 						printf("%s: skipping empty range\n",
308 						       pnp_eisaformat(id));
309 					}
310 					continue;
311 				}
312 				if (bootverbose) {
313 					printf("%s: adding memory32 range "
314 					       "%#x-%#x, size=%#x, "
315 					       "align=%#x\n",
316 					       pnp_eisaformat(id),
317 					       I32(resinfo + 1),
318 					       I32(resinfo + 5)
319 					       + I32(resinfo + 13) - 1,
320 					       I32(resinfo + 13),
321 					       I32(resinfo + 9));
322 				}
323 
324 				if (config->ic_nmem == ISA_NMEM) {
325 					device_printf(parent, "too many memory ranges\n");
326 					scanning = 0;
327 					break;
328 				}
329 
330 				config->ic_mem[config->ic_nmem].ir_start =
331 					I32(resinfo + 1);
332 				config->ic_mem[config->ic_nmem].ir_end =
333 					I32(resinfo + 5)
334 					+ I32(resinfo + 13) - 1;
335 				config->ic_mem[config->ic_nmem].ir_size =
336 					I32(resinfo + 13);
337 				config->ic_mem[config->ic_nmem].ir_align =
338 					I32(resinfo + 9);
339 				config->ic_nmem++;
340 				break;
341 
342 			case PNP_TAG_MEMORY32_FIXED:
343 				if (I32(resinfo + 5) == 0) {
344 					if (bootverbose) {
345 						printf("%s: skipping empty range\n",
346 						       pnp_eisaformat(id));
347 					}
348 					continue;
349 				}
350 				if (bootverbose) {
351 					printf("%s: adding fixed memory32 range "
352 					       "%#x-%#x, size=%#x\n",
353 					       pnp_eisaformat(id),
354 					       I32(resinfo + 1),
355 					       I32(resinfo + 1)
356 					       + I32(resinfo + 5) - 1,
357 					       I32(resinfo + 5));
358 				}
359 
360 				if (config->ic_nmem == ISA_NMEM) {
361 					device_printf(parent, "too many memory ranges\n");
362 					scanning = 0;
363 					break;
364 				}
365 
366 				config->ic_mem[config->ic_nmem].ir_start =
367 					I32(resinfo + 1);
368 				config->ic_mem[config->ic_nmem].ir_end =
369 					I32(resinfo + 1)
370 					+ I32(resinfo + 5) - 1;
371 				config->ic_mem[config->ic_nmem].ir_size =
372 					I32(resinfo + 5);
373 				config->ic_mem[config->ic_nmem].ir_align = 1;
374 				config->ic_nmem++;
375 				break;
376 
377 			default:
378 				/* Skip this resource */
379 				device_printf(parent, "unexpected large tag %d\n",
380 					      PNP_SRES_NUM(tag));
381 			}
382 		}
383 	}
384 	if(ncfgs == 1) {
385 		/* Single config without dependants */
386 		(void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
387 		free(configs, M_DEVBUF);
388 		return;
389 	}
390 	/* Cycle through dependant configs merging primary details */
391 	for(i = 1; i < ncfgs; i++) {
392 		int j;
393 		config = &configs[i];
394 		for(j = 0; j < configs[0].ic_nmem; j++) {
395 			if (config->ic_nmem == ISA_NMEM) {
396 				device_printf(parent, "too many memory ranges\n");
397 				free(configs, M_DEVBUF);
398 				return;
399 			}
400 			config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
401 			config->ic_nmem++;
402 		}
403 		for(j = 0; j < configs[0].ic_nport; j++) {
404 			if (config->ic_nport == ISA_NPORT) {
405 				device_printf(parent, "too many port ranges\n");
406 				free(configs, M_DEVBUF);
407 				return;
408 			}
409 			config->ic_port[config->ic_nport] = configs[0].ic_port[j];
410 			config->ic_nport++;
411 		}
412 		for(j = 0; j < configs[0].ic_nirq; j++) {
413 			if (config->ic_nirq == ISA_NIRQ) {
414 				device_printf(parent, "too many irq ranges\n");
415 				free(configs, M_DEVBUF);
416 				return;
417 			}
418 			config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
419 			config->ic_nirq++;
420 		}
421 		for(j = 0; j < configs[0].ic_ndrq; j++) {
422 			if (config->ic_ndrq == ISA_NDRQ) {
423 				device_printf(parent, "too many drq ranges\n");
424 				free(configs, M_DEVBUF);
425 				return;
426 			}
427 			config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
428 			config->ic_ndrq++;
429 		}
430 		(void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
431 	}
432 	free(configs, M_DEVBUF);
433 }
434