]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/pnpinfo/pnpinfo.c
Merge llvm-project main llvmorg-18-init-16595-g7c00a5be5cde
[FreeBSD/FreeBSD.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/time.h>
28
29 #include <err.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <string.h>
35
36 #include <machine/cpufunc.h>
37
38 #include <isa/pnpreg.h>
39
40 #ifdef DEBUG
41 #define DEB(x) x
42 #else
43 #define DEB(x)
44 #endif
45 #define DDB(x) x
46
47 void
48 pnp_write(int d, u_char r)
49 {
50     outb(_PNP_ADDRESS, d);
51     outb(_PNP_WRITE_DATA, r);
52 }
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_short
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(int i);
75 void send_Initiation_LFSR(void);
76 int get_serial(u_char *data);
77 int get_resource_info(u_char *buffer, int len);
78 int handle_small_res(u_char *resinfo, int item, int len);
79 void handle_large_res(u_char *resinfo, int item, int len);
80 void dump_resdata(u_char *data, int csn);
81 int isolation_protocol(void);
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(void)
111 {
112     int cur, i;
113
114     pnp_write(PNP_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, PNP_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, PNP_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(int x)
190 {
191     char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
192
193     switch (x & 0x3) {
194     case 0:
195         s1="8-bit";
196         break;
197     case 1:
198         s1="8/16-bit";
199         break;
200     case 2:
201         s1="16-bit";
202         break;
203 #ifdef DIAGNOSTIC
204     case 3:
205         s1="Reserved";
206         break;
207 #endif
208     }
209
210     s2 = (x & 0x4) ? "bus master" : "not a bus master";
211
212     s3 = (x & 0x8) ? "count by byte" : "";
213
214     s4 = (x & 0x10) ? "count by word" : "";
215
216     switch ((x & 0x60) >> 5) {
217     case 0:
218         s5="Compatibility mode";
219         break;
220     case 1:
221         s5="Type A";
222         break;
223     case 2:
224         s5="Type B";
225         break;
226     case 3:
227         s5="Type F";
228         break;
229     }
230     printf("\t%s, %s, %s, %s, %s\n",s1,s2,s3,s4,s5);
231 }
232
233
234 void
235 report_memory_info(int x)
236 {
237     if (x & 0x1)
238         printf ("Memory Range: Writeable\n");
239     else
240         printf ("Memory Range: Not writeable (ROM)\n");
241
242     if (x & 0x2)
243         printf ("Memory Range: Read-cacheable, write-through\n");
244     else
245         printf ("Memory Range: Non-cacheable\n");
246
247     if (x & 0x4)
248         printf ("Memory Range: Decode supports high address\n");
249     else
250         printf ("Memory Range: Decode supports range length\n");
251
252     switch ((x & 0x18) >> 3) {
253     case 0:
254         printf ("Memory Range: 8-bit memory only\n");
255         break;
256     case 1:
257         printf ("Memory Range: 16-bit memory only\n");
258         break;
259     case 2:
260         printf ("Memory Range: 8-bit and 16-bit memory supported\n");
261         break;
262 #ifdef DIAGNOSTIC
263     case 3:
264         printf ("Memory Range: Reserved\n");
265         break;
266 #endif
267     }
268
269     if (x & 0x20)
270         printf ("Memory Range: Memory is shadowable\n");
271     else
272         printf ("Memory Range: Memory is not shadowable\n");
273
274     if (x & 0x40)
275         printf ("Memory Range: Memory is an expansion ROM\n");
276     else
277         printf ("Memory Range: Memory is not an expansion ROM\n");
278
279 #ifdef DIAGNOSTIC
280     if (x & 0x80)
281         printf ("Memory Range: Reserved (Device is brain-damaged)\n");
282 #endif
283 }
284
285
286 /*
287  *  Small Resource Tag Handler
288  *
289  *  Returns 1 if checksum was valid (and an END_TAG was received).
290  *  Returns -1 if checksum was invalid (and an END_TAG was received).
291  *  Returns 0 for other tags.
292  */
293 int
294 handle_small_res(u_char *resinfo, int item, int len)
295 {
296     int i;
297
298     DEB(printf("*** ITEM 0x%04x len %d detected\n", item, len));
299
300     switch (item) {
301     default:
302         printf("*** ITEM 0x%02x detected\n", item);
303         break;
304     case PNP_TAG_VERSION:
305         printf("PnP Version %d.%d, Vendor Version %d\n",
306             resinfo[0] >> 4, resinfo[0] & (0xf), resinfo[1]);
307         break;
308     case PNP_TAG_LOGICAL_DEVICE:
309         printf("\nLogical Device ID: %c%c%c%02x%02x 0x%08x #%d\n",
310                 ((resinfo[0] & 0x7c) >> 2) + 64,
311                 (((resinfo[0] & 0x03) << 3) |
312                 ((resinfo[1] & 0xe0) >> 5)) + 64,
313                 (resinfo[1] & 0x1f) + 64,
314                 resinfo[2], resinfo[3], *(int *)(resinfo),
315                 logdevs++);
316
317         if (resinfo[4] & 0x1)
318             printf ("\tDevice powers up active\n"); /* XXX */
319         if (resinfo[4] & 0x2)
320             printf ("\tDevice supports I/O Range Check\n");
321         if (resinfo[4] > 0x3)
322             printf ("\tReserved register funcs %02x\n",
323                 resinfo[4]);
324
325         if (len == 6)
326             printf("\tVendor register funcs %02x\n", resinfo[5]);
327         break;
328     case PNP_TAG_COMPAT_DEVICE:
329         printf("Compatible Device ID: %c%c%c%02x%02x (%08x)\n",
330                 ((resinfo[0] & 0x7c) >> 2) + 64,
331                 (((resinfo[0] & 0x03) << 3) |
332                 ((resinfo[1] & 0xe0) >> 5)) + 64,
333                 (resinfo[1] & 0x1f) + 64,
334                 resinfo[2], resinfo[3], *(int *)resinfo);
335         break;
336     case PNP_TAG_IRQ_FORMAT:
337         printf("    IRQ: ");
338
339         for (i = 0; i < 8; i++)
340             if (resinfo[0] & (1<<i))
341                 printf("%d ", i);
342         for (i = 0; i < 8; i++)
343             if (resinfo[1] & (1<<i))
344                 printf("%d ", i + 8);
345         if (len == 3) {
346             if (resinfo[2] & 0x1)
347                 printf("IRQ: High true edge sensitive\n");
348             if (resinfo[2] & 0x2)
349                 printf("IRQ: Low true edge sensitive\n");
350             if (resinfo[2] & 0x4)
351                 printf("IRQ: High true level sensitive\n");
352             if (resinfo[2] & 0x8)
353                 printf("IRQ: Low true level sensitive\n");
354         } else {
355             printf(" - only one type (true/edge)\n");
356         }
357         break;
358     case PNP_TAG_DMA_FORMAT:
359         printf("    DMA: channel(s) ");
360         for (i = 0; i < 8; i++)
361             if (resinfo[0] & (1<<i))
362                 printf("%d ", i);
363         printf ("\n");
364         report_dma_info (resinfo[1]);
365         break;
366     case PNP_TAG_START_DEPENDANT:
367         printf("TAG Start DF\n");
368         if (len == 1) {
369             switch (resinfo[0]) {
370             case 0:
371                 printf("Good Configuration\n");
372                 break;
373             case 1:
374                 printf("Acceptable Configuration\n");
375                 break;
376             case 2:
377                 printf("Sub-optimal Configuration\n");
378                 break;
379             }
380         }
381         break;
382     case PNP_TAG_END_DEPENDANT:
383         printf("TAG End DF\n");
384         break;
385     case PNP_TAG_IO_RANGE:
386         printf("    I/O Range 0x%x .. 0x%x, alignment 0x%x, len 0x%x\n",
387             resinfo[1] + (resinfo[2] << 8),
388             resinfo[3] + (resinfo[4] << 8),
389             resinfo[5], resinfo[6] );
390         if (resinfo[0])
391             printf("\t[16-bit addr]\n");
392         else
393             printf("\t[not 16-bit addr]\n");
394         break;
395     case PNP_TAG_IO_FIXED:
396         printf ("    FIXED I/O base address 0x%x length 0x%x\n",
397             resinfo[0] + ( (resinfo[1] & 3 ) << 8), /* XXX */
398             resinfo[2]);
399         break;
400 #ifdef DIAGNOSTIC
401     case PNP_TAG_RESERVED:
402         printf("Reserved Tag Detected\n");
403         break;
404 #endif
405     case PNP_TAG_VENDOR:
406         printf("*** Small Vendor Tag Detected\n");
407         break;
408     case PNP_TAG_END:
409         printf("End Tag\n\n");
410         /* XXX Record and Verify Checksum */
411         return 1;
412         break;
413     }
414     return 0;
415 }
416
417
418 void
419 handle_large_res(u_char *resinfo, int item, int len)
420 {
421     int i;
422
423     DEB(printf("*** Large ITEM %d len %d found\n", item, len));
424     switch (item) {
425     case PNP_TAG_MEMORY_RANGE:
426         report_memory_info(resinfo[0]);
427         printf("Memory range minimum address: 0x%x\n",
428                 (resinfo[1] << 8) + (resinfo[2] << 16));
429         printf("Memory range maximum address: 0x%x\n",
430                 (resinfo[3] << 8) + (resinfo[4] << 16));
431         printf("Memory range base alignment: 0x%x\n",
432                 (i = (resinfo[5] + (resinfo[6] << 8))) ? i : (1 << 16));
433         printf("Memory range length: 0x%x\n",
434                 (resinfo[7] + (resinfo[8] << 8)) * 256);
435         break;
436     case PNP_TAG_ID_ANSI:
437         printf("Device Description: ");
438
439         for (i = 0; i < len; i++) {
440             if (resinfo[i]) /* XXX */
441                 printf("%c", resinfo[i]);
442         }
443         printf("\n");
444         break;
445     case PNP_TAG_ID_UNICODE:
446         printf("ID String Unicode Detected (Undefined)\n");
447         break;
448     case PNP_TAG_LARGE_VENDOR:
449         printf("Large Vendor Defined Detected\n");
450         break;
451     case PNP_TAG_MEMORY32_RANGE:
452         printf("32bit Memory Range Desc Unimplemented\n");
453         break;
454     case PNP_TAG_MEMORY32_FIXED:
455         printf("32bit Fixed Location Desc Unimplemented\n");
456         break;
457 #ifdef DIAGNOSTIC
458     case PNP_TAG_LARGE_RESERVED:
459         printf("Large Reserved Tag Detected\n");
460         break;
461 #endif
462     }
463 }
464
465
466 /*
467  * Dump all the information about configurations.
468  */
469 void
470 dump_resdata(u_char *data, int csn)
471 {
472     int i, large_len;
473
474     u_char tag, *resinfo;
475
476     DDB(printf("\nCard assigned CSN #%d\n", csn));
477     printf("Vendor ID %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
478             ((data[0] & 0x7c) >> 2) + 64,
479             (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
480             (data[1] & 0x1f) + 64, data[2], data[3],
481             *(int *)&(data[0]),
482             *(int *)&(data[4]));
483
484     pnp_write(PNP_SET_CSN, csn); /* Move this out of this function XXX */
485     outb(_PNP_ADDRESS, PNP_STATUS);
486
487     /* Allows up to 1kb of Resource Info,  Should be plenty */
488     for (i = 0; i < 1024; i++) {
489         if (!get_resource_info(&tag, 1))
490             break;
491
492         if (PNP_RES_TYPE(tag) == 0) {
493             /* Handle small resouce data types */
494
495             resinfo = malloc(PNP_SRES_LEN(tag));
496             if (!get_resource_info(resinfo, PNP_SRES_LEN(tag)))
497                 break;
498
499             if (handle_small_res(resinfo, PNP_SRES_NUM(tag), PNP_SRES_LEN(tag)) == 1)
500                 break;
501             free(resinfo);
502         } else {
503             /* Handle large resouce data types */
504             u_char buf[2];
505             if (!get_resource_info((char *)buf, 2))
506                 break;
507             large_len = (buf[1] << 8) + buf[0];
508
509             resinfo = malloc(large_len);
510             if (!get_resource_info(resinfo, large_len))
511                 break;
512
513             handle_large_res(resinfo, PNP_LRES_NUM(tag), large_len);
514             free(resinfo);
515         }
516     }
517     printf("Successfully got %d resources, %d logical fdevs\n", i,
518             logdevs);
519     printf("-- card select # 0x%04x\n", pnp_read(PNP_SET_CSN));
520     printf("\nCSN %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
521             ((data[0] & 0x7c) >> 2) + 64,
522             (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
523             (data[1] & 0x1f) + 64, data[2], data[3],
524             *(int *)&(data[0]),
525             *(int *)&(data[4]));
526
527     for (i=0; i<logdevs; i++) {
528         int j;
529
530         pnp_write(PNP_SET_LDN, i);
531
532         printf("\nLogical device #%d\n", pnp_read(PNP_SET_LDN) );
533         printf("IO: ");
534         for (j=0; j<8; j++)
535             printf(" 0x%02x%02x", pnp_read(PNP_IO_BASE_HIGH(j)),
536                 pnp_read(PNP_IO_BASE_LOW(j)));
537         printf("\nIRQ %d %d\n",
538             pnp_read(PNP_IRQ_LEVEL(0)), pnp_read(PNP_IRQ_LEVEL(1)) );
539         printf("DMA %d %d\n",
540             pnp_read(PNP_DMA_CHANNEL(0)), pnp_read(PNP_DMA_CHANNEL(1)) );
541         printf("IO range check 0x%02x activate 0x%02x\n",
542             pnp_read(PNP_IO_RANGE_CHECK), pnp_read(PNP_ACTIVATE) );
543     }
544 }
545
546
547 /*
548  * Run the isolation protocol. Use rd_port as the READ_DATA port
549  * value (caller should try multiple READ_DATA locations before giving
550  * up). Upon exiting, all cards are aware that they should use rd_port
551  * as the READ_DATA port;
552  *
553  */
554 int
555 isolation_protocol(void)
556 {
557     int csn;
558     u_char data[9];
559
560     send_Initiation_LFSR();
561
562     /* Reset CSN for All Cards */
563     pnp_write(PNP_CONFIG_CONTROL, 0x04);
564
565     for (csn = 1; (csn < PNP_MAX_CARDS); csn++) {
566         /* Wake up cards without a CSN */
567         logdevs = 0 ;
568         pnp_write(PNP_WAKE, 0);
569         pnp_write(PNP_SET_RD_DATA, rd_port);
570         outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
571         DELAY(1000);    /* Delay 1 msec */
572
573         if (get_serial(data))
574             dump_resdata(data, csn);
575         else
576             break;
577     }
578     return csn - 1;
579 }
580
581
582 int
583 main(int argc, char **argv)
584 {
585     int num_pnp_devs;
586
587 #ifdef __i386__
588     /* Hey what about a i386_iopl() call :) */
589     if (open("/dev/io", O_RDONLY) < 0)
590         errx(1, "can't get I/O privilege");
591 #endif
592
593     printf("Checking for Plug-n-Play devices...\n");
594
595     /* Try various READ_DATA ports from 0x203-0x3ff */
596     for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) {
597         DEB(printf("Trying Read_Port at %x...\n", (rd_port << 2) | 0x3) );
598         num_pnp_devs = isolation_protocol();
599         if (num_pnp_devs)
600             break;
601     }
602     if (!num_pnp_devs) {
603         printf("No Plug-n-Play devices were found\n");
604         return (0);
605     }
606     return (0);
607 }