xref: /freebsd/contrib/pnpinfo/pnpinfo.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
1 /*
2  * Copyright (c) 1996, Sujal M. Patel
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  *      $Id: pnpinfo.c,v 1.16 1996/05/05 23:56:38 smpatel Exp $
27  */
28 
29 #include <sys/time.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 
37 #include <machine/cpufunc.h>
38 
39 #include <i386/isa/pnp.h>
40 
41 #ifdef DEBUG
42 #define	DEB(x) x
43 #else
44 #define DEB(x)
45 #endif
46 #define DDB(x) x
47 
48 void
49 pnp_write(int d, u_char r)
50 {
51     outb (_PNP_ADDRESS, d);
52     outb (_PNP_WRITE_DATA, r);
53 }
54 /* The READ_DATA port that we are using currently */
55 static int rd_port;
56 
57 u_char
58 pnp_read(int d)
59 {
60     outb(_PNP_ADDRESS, d);
61     return inb( (rd_port << 2) + 3) & 0xff;
62 }
63 
64 u_char
65 pnp_readw(int d)
66 {
67     int c = pnp_read(d) << 8 ;
68     c |= pnp_read(d+1);
69     return c;
70 }
71 
72 int logdevs=0;
73 
74 void DELAY __P((int i));
75 void send_Initiation_LFSR();
76 int get_serial __P((u_char *data));
77 int get_resource_info __P((u_char *buffer, int len));
78 int handle_small_res __P((u_char *resinfo, int item, int len));
79 void handle_large_res __P((u_char *resinfo, int item, int len));
80 void dump_resdata __P((u_char *data, int csn));
81 int isolation_protocol();
82 
83 
84 /*
85  * DELAY does accurate delaying in user-space.
86  * This function busy-waits.
87  */
88 void
89 DELAY (int i)
90 {
91     struct timeval t;
92     long start, stop;
93 
94     i *= 4;
95 
96     gettimeofday (&t, NULL);
97     start = t.tv_sec * 1000000 + t.tv_usec;
98     do {
99 	gettimeofday (&t, NULL);
100 	stop = t.tv_sec * 1000000 + t.tv_usec;
101     } while (start + i > stop);
102 }
103 
104 
105 /*
106  * Send Initiation LFSR as described in "Plug and Play ISA Specification,
107  * Intel May 94."
108  */
109 void
110 send_Initiation_LFSR()
111 {
112     int cur, i;
113 
114     pnp_write(CONFIG_CONTROL, 0x2);
115 
116     /* Reset the LSFR */
117     outb(_PNP_ADDRESS, 0);
118     outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
119 
120     cur = 0x6a;
121 
122     for (i = 0; i < 32; i++) {
123 	outb(_PNP_ADDRESS, cur);
124 	cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
125     }
126 }
127 
128 /*
129  * Get the device's serial number.  Returns 1 if the serial is valid.
130  */
131 int
132 get_serial(u_char *data)
133 {
134     int i, bit, valid = 0, sum = 0x6a;
135 
136     bzero(data, sizeof(char) * 9);
137 
138     for (i = 0; i < 72; i++) {
139 	bit = inb((rd_port << 2) | 0x3) == 0x55;
140 	DELAY(250);	/* Delay 250 usec */
141 
142 	/* Can't Short Circuit the next evaluation, so 'and' is last */
143 	bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit;
144 	DELAY(250);	/* Delay 250 usec */
145 
146 	valid = valid || bit;
147 
148 	if (i < 64)
149 	    sum = (sum >> 1) |
150 		(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
151 
152 	data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
153     }
154 
155     valid = valid && (data[8] == sum);
156 
157     return valid;
158 }
159 
160 
161 /*
162  * Fill's the buffer with resource info from the device.
163  * Returns 0 if the device fails to report
164  */
165 int
166 get_resource_info(u_char *buffer, int len)
167 {
168     int i, j;
169 
170     for (i = 0; i < len; i++) {
171 	outb(_PNP_ADDRESS, STATUS);
172 	for (j = 0; j < 100; j++) {
173 	    if ((inb((rd_port << 2) | 0x3)) & 0x1)
174 		break;
175 	    DELAY(1);
176 	}
177 	if (j == 100) {
178 	    printf("PnP device failed to report resource data\n");
179 	    return 0;
180 	}
181 	outb(_PNP_ADDRESS, RESOURCE_DATA);
182 	buffer[i] = inb((rd_port << 2) | 0x3);
183 	DEB(printf("--- get_resource_info: got 0x%02x\n",(unsigned)buffer[i]));
184     }
185     return 1;
186 }
187 
188 void
189 report_dma_info (x)
190 	int x;
191 {
192     char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
193 
194     switch (x & 0x3) {
195     case 0:
196 	s1="8-bit";
197 	break;
198     case 1:
199 	s1="8/16-bit";
200 	break;
201     case 2:
202 	s1="16-bit";
203 	break;
204 #ifdef DIAGNOSTIC
205     case 3:
206 	s1="Reserved";
207 	break;
208 #endif
209     }
210 
211     s2 = (x & 0x4) ? "bus master" : "not a bus master";
212 
213     s3 = (x & 0x8) ? "count by byte" : "";
214 
215     s4 = (x & 0x10) ? "count by word" : "";
216 
217     switch ((x & 0x60) >> 5) {
218     case 0:
219 	s5="Compatibility mode";
220 	break;
221     case 1:
222 	s5="Type A";
223 	break;
224     case 2:
225 	s5="Type B";
226 	break;
227     case 3:
228 	s5="Type F";
229 	break;
230     }
231     printf("\t%s, %s, %s, %s, %s\n",s1,s2,s3,s4,s5);
232 }
233 
234 
235 void
236 report_memory_info (int x)
237 {
238     if (x & 0x1)
239 	printf ("Memory Range: Writeable\n");
240     else
241 	printf ("Memory Range: Not writeable (ROM)\n");
242 
243     if (x & 0x2)
244 	printf ("Memory Range: Read-cacheable, write-through\n");
245     else
246 	printf ("Memory Range: Non-cacheable\n");
247 
248     if (x & 0x4)
249 	printf ("Memory Range: Decode supports high address\n");
250     else
251 	printf ("Memory Range: Decode supports range length\n");
252 
253     switch ((x & 0x18) >> 3) {
254     case 0:
255 	printf ("Memory Range: 8-bit memory only\n");
256 	break;
257     case 1:
258 	printf ("Memory Range: 16-bit memory only\n");
259 	break;
260     case 2:
261 	printf ("Memory Range: 8-bit and 16-bit memory supported\n");
262 	break;
263 #ifdef DIAGNOSTIC
264     case 3:
265 	printf ("Memory Range: Reserved\n");
266 	break;
267 #endif
268     }
269 
270     if (x & 0x20)
271 	printf ("Memory Range: Memory is shadowable\n");
272     else
273 	printf ("Memory Range: Memory is not shadowable\n");
274 
275     if (x & 0x40)
276 	printf ("Memory Range: Memory is an expansion ROM\n");
277     else
278 	printf ("Memory Range: Memory is not an expansion ROM\n");
279 
280 #ifdef DIAGNOSTIC
281     if (x & 0x80)
282 	printf ("Memory Range: Reserved (Device is brain-damaged)\n");
283 #endif
284 }
285 
286 
287 /*
288  *  Small Resource Tag Handler
289  *
290  *  Returns 1 if checksum was valid (and an END_TAG was received).
291  *  Returns -1 if checksum was invalid (and an END_TAG was received).
292  *  Returns 0 for other tags.
293  */
294 int
295 handle_small_res(u_char *resinfo, int item, int len)
296 {
297     int i;
298 
299     DEB(printf("*** ITEM 0x%04x len %d detected\n", item, len));
300 
301     switch (item) {
302     default:
303 	printf("*** ITEM 0x%02x detected\n", item);
304 	break;
305     case PNP_VERSION:
306 	printf("PnP Version %d.%d, Vendor Version %d\n",
307 	    resinfo[0] >> 4, resinfo[0] & (0xf), resinfo[1]);
308 	break;
309     case LOG_DEVICE_ID:
310 	printf("\nLogical Device ID: %c%c%c%02x%02x 0x%08x #%d\n",
311 		((resinfo[0] & 0x7c) >> 2) + 64,
312 		(((resinfo[0] & 0x03) << 3) |
313 		((resinfo[1] & 0xe0) >> 5)) + 64,
314 		(resinfo[1] & 0x1f) + 64,
315 		resinfo[2], resinfo[3], *(int *)(resinfo),
316 		logdevs++);
317 
318 	if (resinfo[4] & 0x1)
319 	    printf ("\tDevice powers up active\n"); /* XXX */
320 	if (resinfo[4] & 0x2)
321 	    printf ("\tDevice supports I/O Range Check\n");
322 	if (resinfo[4] > 0x3)
323 	    printf ("\tReserved register funcs %02x\n",
324 		resinfo[4]);
325 
326 	if (len == 6)
327 	    printf("\tVendor register funcs %02x\n", resinfo[5]);
328 	break;
329     case COMP_DEVICE_ID:
330 	printf("Compatible Device ID: %c%c%c%02x%02x (%08x)\n",
331 		((resinfo[0] & 0x7c) >> 2) + 64,
332 		(((resinfo[0] & 0x03) << 3) |
333 		((resinfo[1] & 0xe0) >> 5)) + 64,
334 		(resinfo[1] & 0x1f) + 64,
335 		resinfo[2], resinfo[3], *(int *)resinfo);
336 	break;
337     case IRQ_FORMAT:
338 	printf("    IRQ: ");
339 
340 	for (i = 0; i < 8; i++)
341 	    if (resinfo[0] & (1<<i))
342 		printf("%d ", i);
343 	for (i = 0; i < 8; i++)
344 	    if (resinfo[1] & (1<<i))
345 		printf("%d ", i + 8);
346 	if (len == 3) {
347 	    if (resinfo[2] & 0x1)
348 		printf("IRQ: High true edge sensitive\n");
349 	    if (resinfo[2] & 0x2)
350 		printf("IRQ: Low true edge sensitive\n");
351 	    if (resinfo[2] & 0x4)
352 		printf("IRQ: High true level sensitive\n");
353 	    if (resinfo[2] & 0x8)
354 		printf("IRQ: Low true level sensitive\n");
355 	} else {
356 	    printf(" - only one type (true/edge)\n");
357 	}
358 	break;
359     case DMA_FORMAT:
360 	printf("    DMA: channel(s) ");
361 	for (i = 0; i < 8; i++)
362 	    if (resinfo[0] & (1<<i))
363 		printf("%d ", i);
364 	printf ("\n");
365 	report_dma_info (resinfo[1]);
366 	break;
367     case START_DEPEND_FUNC:
368 	printf("TAG Start DF\n");
369 	if (len == 1) {
370 	    switch (resinfo[0]) {
371 	    case 0:
372 		printf("Good Configuration\n");
373 		break;
374 	    case 1:
375 		printf("Acceptable Configuration\n");
376 		break;
377 	    case 2:
378 		printf("Sub-optimal Configuration\n");
379 		break;
380 	    }
381 	}
382 	break;
383     case END_DEPEND_FUNC:
384 	printf("TAG End DF\n");
385 	break;
386     case IO_PORT_DESC:
387 	printf("    I/O Range 0x%x .. 0x%x, alignment 0x%x, len 0x%x\n",
388 	    resinfo[1] + (resinfo[2] << 8),
389 	    resinfo[3] + (resinfo[4] << 8),
390 	    resinfo[5], resinfo[6] );
391 	if (resinfo[0])
392 	    printf("\t[16-bit addr]\n");
393 	else
394 	    printf("\t[not 16-bit addr]\n");
395 	break;
396     case FIXED_IO_PORT_DESC:
397 	printf ("    FIXED I/O base address 0x%x length 0x%x\n",
398 	    resinfo[0] + ( (resinfo[1] & 3 ) << 8), /* XXX */
399 	    resinfo[2]);
400 	break;
401 #ifdef DIAGNOSTIC
402     case SM_RES_RESERVED:
403 	printf("Reserved Tag Detected\n");
404 	break;
405 #endif
406     case SM_VENDOR_DEFINED:
407 	printf("*** Small Vendor Tag Detected\n");
408 	break;
409     case END_TAG:
410 	printf("End Tag\n\n");
411 	/* XXX Record and Verify Checksum */
412 	return 1;
413 	break;
414     }
415     return 0;
416 }
417 
418 
419 void
420 handle_large_res(u_char *resinfo, int item, int len)
421 {
422     int i;
423 
424     DEB(printf("*** Large ITEM %d len %d found\n", item, len));
425     switch (item) {
426     case MEMORY_RANGE_DESC:
427 	report_memory_info(resinfo[0]);
428 	printf("Memory range minimum address: 0x%x\n",
429 		(resinfo[1] << 8) + (resinfo[2] << 16));
430 	printf("Memory range maximum address: 0x%x\n",
431 		(resinfo[3] << 8) + (resinfo[4] << 16));
432 	printf("Memory range base alignment: 0x%x\n",
433 		(i = (resinfo[5] + (resinfo[6] << 8))) ? i : (1 << 16));
434 	printf("Memory range length: 0x%x\n",
435 		(resinfo[7] + (resinfo[8] << 8)) * 256);
436 	break;
437     case ID_STRING_ANSI:
438 	printf("Device Description: ");
439 
440 	for (i = 0; i < len; i++) {
441 	    if (resinfo[i]) /* XXX */
442 		printf("%c", resinfo[i]);
443 	}
444 	printf("\n");
445 	break;
446     case ID_STRING_UNICODE:
447 	printf("ID String Unicode Detected (Undefined)\n");
448 	break;
449     case LG_VENDOR_DEFINED:
450 	printf("Large Vendor Defined Detected\n");
451 	break;
452     case _32BIT_MEM_RANGE_DESC:
453 	printf("32bit Memory Range Desc Unimplemented\n");
454 	break;
455     case _32BIT_FIXED_LOC_DESC:
456 	printf("32bit Fixed Location Desc Unimplemented\n");
457 	break;
458     case LG_RES_RESERVED:
459 	printf("Large Reserved Tag Detected\n");
460 	break;
461     }
462 }
463 
464 
465 /*
466  * Dump all the information about configurations.
467  */
468 void
469 dump_resdata(u_char *data, int csn)
470 {
471     int i, large_len;
472 
473     u_char tag, *resinfo;
474 
475     DDB(printf("\nCard assigned CSN #%d\n", csn));
476     printf("Vendor ID %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
477 	    ((data[0] & 0x7c) >> 2) + 64,
478 	    (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
479 	    (data[1] & 0x1f) + 64, data[2], data[3],
480 	    *(int *)&(data[0]),
481 	    *(int *)&(data[4]));
482 
483     pnp_write(SET_CSN, csn); /* Move this out of this function XXX */
484     outb(_PNP_ADDRESS, STATUS);
485 
486     /* Allows up to 1kb of Resource Info,  Should be plenty */
487     for (i = 0; i < 1024; i++) {
488 	if (!get_resource_info(&tag, 1))
489 	    break;
490 
491 #define TYPE	(tag >> 7)
492 #define	S_ITEM	(tag >> 3)
493 #define S_LEN	(tag & 0x7)
494 #define	L_ITEM	(tag & 0x7f)
495 
496 	if (TYPE == 0) {
497 	    /* Handle small resouce data types */
498 
499 	    resinfo = malloc(S_LEN);
500 	    if (!get_resource_info(resinfo, S_LEN))
501 		break;
502 
503 	    if (handle_small_res(resinfo, S_ITEM, S_LEN) == 1)
504 		break;
505 	    free(resinfo);
506 	} else {
507 	    /* Handle large resouce data types */
508 
509 	    if (!get_resource_info((char *) &large_len, 2))
510 		break;
511 
512 	    resinfo = malloc(large_len);
513 	    if (!get_resource_info(resinfo, large_len))
514 		break;
515 
516 	    handle_large_res(resinfo, L_ITEM, large_len);
517 	    free(resinfo);
518 	}
519     }
520     printf("Successfully got %d resources, %d logical fdevs\n", i,
521 	    logdevs);
522     printf("-- card select # 0x%04x\n", pnp_read(SET_CSN));
523     printf("\nCSN %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
524 	    ((data[0] & 0x7c) >> 2) + 64,
525 	    (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
526 	    (data[1] & 0x1f) + 64, data[2], data[3],
527 	    *(int *)&(data[0]),
528 	    *(int *)&(data[4]));
529 
530     for (i=0; i<logdevs; i++) {
531 	int j;
532 
533 	pnp_write(SET_LDN, i);
534 
535 	printf("\nLogical device #%d\n", pnp_read(SET_LDN) );
536 	printf("IO: ");
537 	for (j=0; j<8; j++)
538 	    printf(" 0x%04x", pnp_readw(IO_CONFIG_BASE + j*2));
539 	printf("\nIRQ %d %d\n",
540 	    pnp_read(IRQ_CONFIG), pnp_read(IRQ_CONFIG+2) );
541 	printf("DMA %d %d\n",
542 	    pnp_read(DRQ_CONFIG), pnp_read(DRQ_CONFIG+1) );
543 	printf("IO range check 0x%02x activate 0x%02x\n",
544 	    pnp_read(IO_RANGE_CHECK), pnp_read(ACTIVATE) );
545     }
546 }
547 
548 
549 /*
550  * Run the isolation protocol. Use rd_port as the READ_DATA port
551  * value (caller should try multiple READ_DATA locations before giving
552  * up). Upon exiting, all cards are aware that they should use rd_port
553  * as the READ_DATA port;
554  *
555  */
556 int
557 isolation_protocol()
558 {
559     int csn;
560     u_char data[9];
561 
562     send_Initiation_LFSR();
563 
564     /* Reset CSN for All Cards */
565     pnp_write(CONFIG_CONTROL, 0x04);
566 
567     for (csn = 1; (csn < MAX_PNP_CARDS); csn++) {
568 	/* Wake up cards without a CSN */
569 	logdevs = 0 ;
570 	pnp_write(WAKE, 0);
571 	pnp_write(SET_RD_DATA, rd_port);
572 	outb(_PNP_ADDRESS, SERIAL_ISOLATION);
573 	DELAY(1000);	/* Delay 1 msec */
574 
575 	if (get_serial(data))
576 	    dump_resdata(data, csn);
577 	else
578 	    break;
579     }
580     return csn - 1;
581 }
582 
583 
584 void
585 main()
586 {
587     int num_pnp_devs;
588 
589     /* Hey what about a i386_iopl() call :) */
590     if (open("/dev/io", O_RDONLY) < 0) {
591 	fprintf (stderr, "pnpinfo: Can't get I/O privilege.\n");
592 	exit (1);
593     }
594     printf("Checking for Plug-n-Play devices...\n");
595 
596     /* Try various READ_DATA ports from 0x203-0x3ff */
597     for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) {
598 	DEB(printf("Trying Read_Port at %x...\n", (rd_port << 2) | 0x3) );
599 	num_pnp_devs = isolation_protocol(rd_port);
600 	if (num_pnp_devs)
601 	    break;
602     }
603     if (!num_pnp_devs) {
604 	printf("No Plug-n-Play devices were found\n");
605 	return;
606     }
607 }
608