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