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