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