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