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