Merge branch 'vendor/TEXINFO'
[dragonfly.git] / contrib / pnpinfo / pnpinfo.c
CommitLineData
984263bc
MD
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 $
38a690d7 27 * $DragonFly: src/contrib/pnpinfo/pnpinfo.c,v 1.3 2003/08/08 04:18:31 dillon Exp $
984263bc
MD
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
38a690d7 40#include <bus/isa/pnpreg.h>
984263bc
MD
41
42#ifdef DEBUG
43#define DEB(x) x
44#else
45#define DEB(x)
46#endif
47#define DDB(x) x
48
20cda864
SW
49static void pnp_write(int, u_char);
50static u_char pnp_read(int);
51static void DELAY(int i);
52static void send_Initiation_LFSR(void);
53static int get_serial(u_char *);
54static int get_resource_info(u_char *, int);
5b8f5447
SW
55static void report_dma_info(int);
56static void report_memory_info(int);
20cda864
SW
57static int handle_small_res(u_char *, int, int);
58static void handle_large_res(u_char *, int, int);
59static void dump_resdata(u_char *, int);
60static int isolation_protocol(void);
61
62/* The READ_DATA port that we are using currently */
63static int rd_port;
64
65int logdevs=0;
66
67static void
984263bc
MD
68pnp_write(int d, u_char r)
69{
70 outb (_PNP_ADDRESS, d);
71 outb (_PNP_WRITE_DATA, r);
72}
73
20cda864 74static u_char
984263bc
MD
75pnp_read(int d)
76{
77 outb(_PNP_ADDRESS, d);
78 return inb( (rd_port << 2) + 3) & 0xff;
79}
80
984263bc
MD
81/*
82 * DELAY does accurate delaying in user-space.
83 * This function busy-waits.
84 */
20cda864
SW
85static void
86DELAY(int i)
984263bc
MD
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 */
20cda864
SW
106static void
107send_Initiation_LFSR(void)
984263bc
MD
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 */
20cda864 128static int
984263bc
MD
129get_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 */
20cda864 162static int
984263bc
MD
163get_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
20cda864
SW
185static void
186report_dma_info(int x)
984263bc 187{
20cda864 188 const char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
984263bc
MD
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
20cda864
SW
231static void
232report_memory_info(int x)
984263bc
MD
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 */
20cda864 290static int
984263bc
MD
291handle_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
20cda864 415static void
984263bc
MD
416handle_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 */
20cda864 466static void
984263bc
MD
467dump_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 */
20cda864
SW
551static int
552isolation_protocol(void)
984263bc
MD
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
579int
20cda864 580main(void)
984263bc
MD
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
984263bc
MD
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) );
20cda864 597 num_pnp_devs = isolation_protocol();
984263bc
MD
598 if (num_pnp_devs)
599 break;
600 }
20cda864 601 if (!num_pnp_devs)
984263bc 602 printf("No Plug-n-Play devices were found\n");
20cda864 603 return 0;
984263bc 604}