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