xref: /dragonfly/sys/bus/isa/pnpparse.c (revision 984263bc)
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: src/sys/isa/pnpparse.c,v 1.2.2.3 2000/11/07 05:53:55 msmith Exp $
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <isa/isavar.h>
36 #include <isa/pnpreg.h>
37 #include <isa/pnpvar.h>
38 
39 #define	MAXDEP	8
40 
41 #define I16(p)	((p)[0] + ((p)[1] << 8))
42 #define I32(p)	(I16(p) + (I16(p+2) << 16))
43 
44 /*
45  * Parse resource data for Logical Devices.
46  *
47  * This function exits as soon as it gets an error reading *ANY*
48  * Resource Data or it reaches the end of Resource Data.
49  */
50 void
51 pnp_parse_resources(device_t dev, u_char *resources, int len)
52 {
53 	device_t parent = device_get_parent(dev);
54 	u_char tag, *resp, *resinfo;
55 	int large_len, scanning = len;
56 	u_int32_t id, compat_id;
57 	struct isa_config *config;
58 	int ncfgs = 1;
59 	int priorities[1 + MAXDEP];
60 	struct isa_config *configs;
61 	char buf[100];
62 	int i;
63 
64 	id = isa_get_logicalid(dev);
65 	configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP),
66 						M_DEVBUF, M_NOWAIT);
67 	if (configs == NULL) {
68 		device_printf(dev, "No memory to parse PNP data\n");
69 		return;
70 	}
71 	bzero(configs, sizeof(*configs) * (1 + MAXDEP));
72 	config = &configs[0];
73 	priorities[0] = 0;
74 	resp = resources;
75 	while (scanning > 0) {
76 		tag = *resp++;
77 		scanning--;
78 		if (PNP_RES_TYPE(tag) == 0) {
79 			/* Small resource */
80 			if (scanning < PNP_SRES_LEN(tag)) {
81 				scanning = 0;
82 				continue;
83 			}
84 			resinfo = resp;
85 			resp += PNP_SRES_LEN(tag);
86 			scanning -= PNP_SRES_LEN(tag);;
87 
88 			switch (PNP_SRES_NUM(tag)) {
89 			case PNP_TAG_COMPAT_DEVICE:
90 				/*
91 				 * Got a compatible device id
92 				 * resource. Should keep a list of
93 				 * compat ids in the device.
94 				 */
95 				bcopy(resinfo, &compat_id, 4);
96 				isa_set_compatid(dev, compat_id);
97 				break;
98 
99 			case PNP_TAG_IRQ_FORMAT:
100 				if (bootverbose) {
101 					printf("%s: adding irq mask %#04x\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 				break;
192 
193 			case PNP_TAG_IO_FIXED:
194 				if (bootverbose) {
195 					printf("%s: adding fixed io range "
196 					       "%#x-%#x, size=%#x, "
197 					       "align=%#x\n",
198 					       pnp_eisaformat(id),
199 					       I16(resinfo),
200 					       I16(resinfo) + resinfo[2] - 1,
201 					       resinfo[2],
202 					       1);
203 				}
204 				if (config->ic_nport == ISA_NPORT) {
205 					device_printf(parent, "too many ports\n");
206 					scanning = 0;
207 					break;
208 				}
209 				config->ic_port[config->ic_nport].ir_start =
210 					I16(resinfo);
211 				config->ic_port[config->ic_nport].ir_end =
212 					I16(resinfo) + resinfo[2] - 1;
213 				config->ic_port[config->ic_nport].ir_size
214 					= resinfo[2];
215 				config->ic_port[config->ic_nport].ir_align = 1;
216 				config->ic_nport++;
217 				break;
218 
219 			case PNP_TAG_END:
220 				if (bootverbose) {
221 					printf("%s: end config\n",
222 					       pnp_eisaformat(id));
223 				}
224 				scanning = 0;
225 				break;
226 
227 			default:
228 				/* Skip this resource */
229 				device_printf(parent, "unexpected small tag %d\n",
230 					      PNP_SRES_NUM(tag));
231 				break;
232 			}
233 		} else {
234 			/* Large resource */
235 			if (scanning < 2) {
236 				scanning = 0;
237 				continue;
238 			}
239 			large_len = I16(resp);
240 			resp += 2;
241 			scanning -= 2;
242 
243 			if (scanning < large_len) {
244 				scanning = 0;
245 				continue;
246 			}
247 			resinfo = resp;
248 			resp += large_len;
249 			scanning -= large_len;
250 
251 			switch (PNP_LRES_NUM(tag)) {
252 			case PNP_TAG_ID_ANSI:
253 				if (large_len > sizeof(buf) - 1)
254 					large_len = sizeof(buf) - 1;
255 				bcopy(resinfo, buf, large_len);
256 
257 				/*
258 				 * Trim trailing spaces and garbage.
259 				 */
260 				while (large_len > 0 && buf[large_len - 1] <= ' ')
261 					large_len--;
262 				buf[large_len] = '\0';
263 				device_set_desc_copy(dev, buf);
264 				break;
265 
266 			case PNP_TAG_MEMORY_RANGE:
267 				if (bootverbose) {
268 					int temp = I16(resinfo + 7) << 8;
269 
270 					printf("%s: adding memory range "
271 					       "%#x-%#x, size=%#x, "
272 					       "align=%#x\n",
273 					       pnp_eisaformat(id),
274 					       I16(resinfo + 1)<<8,
275 					       (I16(resinfo + 3)<<8) + temp - 1,
276 					       temp,
277 					       I16(resinfo + 5));
278 				}
279 
280 				if (config->ic_nmem == ISA_NMEM) {
281 					device_printf(parent, "too many memory ranges\n");
282 					scanning = 0;
283 					break;
284 				}
285 
286 				config->ic_mem[config->ic_nmem].ir_start =
287 					I16(resinfo + 1)<<8;
288 				config->ic_mem[config->ic_nmem].ir_end =
289 					(I16(resinfo + 3)<<8)
290 					+ (I16(resinfo + 7) << 8) - 1;
291 				config->ic_mem[config->ic_nmem].ir_size =
292 					I16(resinfo + 7) << 8;
293 				config->ic_mem[config->ic_nmem].ir_align =
294 					I16(resinfo + 5);
295 				if (!config->ic_mem[config->ic_nmem].ir_align)
296 					config->ic_mem[config->ic_nmem]
297 						.ir_align = 0x10000;
298 				config->ic_nmem++;
299 				break;
300 
301 			case PNP_TAG_MEMORY32_RANGE:
302 				if (I32(resinfo + 13) == 0) {
303 					if (bootverbose) {
304 						printf("%s: skipping empty range\n",
305 						       pnp_eisaformat(id));
306 					}
307 					continue;
308 				}
309 				if (bootverbose) {
310 					printf("%s: adding memory32 range "
311 					       "%#x-%#x, size=%#x, "
312 					       "align=%#x\n",
313 					       pnp_eisaformat(id),
314 					       I32(resinfo + 1),
315 					       I32(resinfo + 5)
316 					       + I32(resinfo + 13) - 1,
317 					       I32(resinfo + 13),
318 					       I32(resinfo + 9));
319 				}
320 
321 				if (config->ic_nmem == ISA_NMEM) {
322 					device_printf(parent, "too many memory ranges\n");
323 					scanning = 0;
324 					break;
325 				}
326 
327 				config->ic_mem[config->ic_nmem].ir_start =
328 					I32(resinfo + 1);
329 				config->ic_mem[config->ic_nmem].ir_end =
330 					I32(resinfo + 5)
331 					+ I32(resinfo + 13) - 1;
332 				config->ic_mem[config->ic_nmem].ir_size =
333 					I32(resinfo + 13);
334 				config->ic_mem[config->ic_nmem].ir_align =
335 					I32(resinfo + 9);
336 				config->ic_nmem++;
337 				break;
338 
339 			case PNP_TAG_MEMORY32_FIXED:
340 				if (I32(resinfo + 5) == 0) {
341 					if (bootverbose) {
342 						printf("%s: skipping empty range\n",
343 						       pnp_eisaformat(id));
344 					}
345 					continue;
346 				}
347 				if (bootverbose) {
348 					printf("%s: adding fixed memory32 range "
349 					       "%#x-%#x, size=%#x\n",
350 					       pnp_eisaformat(id),
351 					       I32(resinfo + 1),
352 					       I32(resinfo + 1)
353 					       + I32(resinfo + 5) - 1,
354 					       I32(resinfo + 5));
355 				}
356 
357 				if (config->ic_nmem == ISA_NMEM) {
358 					device_printf(parent, "too many memory ranges\n");
359 					scanning = 0;
360 					break;
361 				}
362 
363 				config->ic_mem[config->ic_nmem].ir_start =
364 					I32(resinfo + 1);
365 				config->ic_mem[config->ic_nmem].ir_end =
366 					I32(resinfo + 1)
367 					+ I32(resinfo + 5) - 1;
368 				config->ic_mem[config->ic_nmem].ir_size =
369 					I32(resinfo + 5);
370 				config->ic_mem[config->ic_nmem].ir_align = 1;
371 				config->ic_nmem++;
372 				break;
373 
374 			default:
375 				/* Skip this resource */
376 				device_printf(parent, "unexpected large tag %d\n",
377 					      PNP_SRES_NUM(tag));
378 			}
379 		}
380 	}
381 	if(ncfgs == 1) {
382 		/* Single config without dependants */
383 		(void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
384 		free(configs, M_DEVBUF);
385 		return;
386 	}
387 	/* Cycle through dependant configs merging primary details */
388 	for(i = 1; i < ncfgs; i++) {
389 		int j;
390 		config = &configs[i];
391 		for(j = 0; j < configs[0].ic_nmem; j++) {
392 			if (config->ic_nmem == ISA_NMEM) {
393 				device_printf(parent, "too many memory ranges\n");
394 				free(configs, M_DEVBUF);
395 				return;
396 			}
397 			config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
398 			config->ic_nmem++;
399 		}
400 		for(j = 0; j < configs[0].ic_nport; j++) {
401 			if (config->ic_nport == ISA_NPORT) {
402 				device_printf(parent, "too many port ranges\n");
403 				free(configs, M_DEVBUF);
404 				return;
405 			}
406 			config->ic_port[config->ic_nport] = configs[0].ic_port[j];
407 			config->ic_nport++;
408 		}
409 		for(j = 0; j < configs[0].ic_nirq; j++) {
410 			if (config->ic_nirq == ISA_NIRQ) {
411 				device_printf(parent, "too many irq ranges\n");
412 				free(configs, M_DEVBUF);
413 				return;
414 			}
415 			config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
416 			config->ic_nirq++;
417 		}
418 		for(j = 0; j < configs[0].ic_ndrq; j++) {
419 			if (config->ic_ndrq == ISA_NDRQ) {
420 				device_printf(parent, "too many drq ranges\n");
421 				free(configs, M_DEVBUF);
422 				return;
423 			}
424 			config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
425 			config->ic_ndrq++;
426 		}
427 		(void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
428 	}
429 	free(configs, M_DEVBUF);
430 }
431