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