pnpinfo.c revision 29616
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 *      $Id: pnpinfo.c,v 1.16 1996/05/05 23:56:38 smpatel Exp $
27 */
28
29#include <sys/time.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include <string.h>
36
37#include <machine/cpufunc.h>
38
39#include <i386/isa/pnp.h>
40
41#ifdef DEBUG
42#define	DEB(x) x
43#else
44#define DEB(x)
45#endif
46#define DDB(x) x
47
48void
49pnp_write(int d, u_char r)
50{
51    outb (_PNP_ADDRESS, d);
52    outb (_PNP_WRITE_DATA, r);
53}
54/* The READ_DATA port that we are using currently */
55static int rd_port;
56
57u_char
58pnp_read(int d)
59{
60    outb(_PNP_ADDRESS, d);
61    return inb( (rd_port << 2) + 3) & 0xff;
62}
63
64u_char
65pnp_readw(int d)
66{
67    int c = pnp_read(d) << 8 ;
68    c |= pnp_read(d+1);
69    return c;
70}
71
72int logdevs=0;
73
74void DELAY __P((int i));
75void send_Initiation_LFSR();
76int get_serial __P((u_char *data));
77int get_resource_info __P((u_char *buffer, int len));
78int handle_small_res __P((u_char *resinfo, int item, int len));
79void handle_large_res __P((u_char *resinfo, int item, int len));
80void dump_resdata __P((u_char *data, int csn));
81int isolation_protocol();
82
83
84/*
85 * DELAY does accurate delaying in user-space.
86 * This function busy-waits.
87 */
88void
89DELAY (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 */
109void
110send_Initiation_LFSR()
111{
112    int cur, i;
113
114    pnp_write(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 */
131int
132get_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 */
165int
166get_resource_info(u_char *buffer, int len)
167{
168    int i, j;
169
170    for (i = 0; i < len; i++) {
171	outb(_PNP_ADDRESS, 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, 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
188void
189report_dma_info (x)
190	int x;
191{
192    char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
193
194    switch (x & 0x3) {
195    case 0:
196	s1="8-bit";
197	break;
198    case 1:
199	s1="8/16-bit";
200	break;
201    case 2:
202	s1="16-bit";
203	break;
204#ifdef DIAGNOSTIC
205    case 3:
206	s1="Reserved";
207	break;
208#endif
209    }
210
211    s2 = (x & 0x4) ? "bus master" : "not a bus master";
212
213    s3 = (x & 0x8) ? "count by byte" : "";
214
215    s4 = (x & 0x10) ? "count by word" : "";
216
217    switch ((x & 0x60) >> 5) {
218    case 0:
219	s5="Compatibility mode";
220	break;
221    case 1:
222	s5="Type A";
223	break;
224    case 2:
225	s5="Type B";
226	break;
227    case 3:
228	s5="Type F";
229	break;
230    }
231    printf("\t%s, %s, %s, %s, %s\n",s1,s2,s3,s4,s5);
232}
233
234
235void
236report_memory_info (int x)
237{
238    if (x & 0x1)
239	printf ("Memory Range: Writeable\n");
240    else
241	printf ("Memory Range: Not writeable (ROM)\n");
242
243    if (x & 0x2)
244	printf ("Memory Range: Read-cacheable, write-through\n");
245    else
246	printf ("Memory Range: Non-cacheable\n");
247
248    if (x & 0x4)
249	printf ("Memory Range: Decode supports high address\n");
250    else
251	printf ("Memory Range: Decode supports range length\n");
252
253    switch ((x & 0x18) >> 3) {
254    case 0:
255	printf ("Memory Range: 8-bit memory only\n");
256	break;
257    case 1:
258	printf ("Memory Range: 16-bit memory only\n");
259	break;
260    case 2:
261	printf ("Memory Range: 8-bit and 16-bit memory supported\n");
262	break;
263#ifdef DIAGNOSTIC
264    case 3:
265	printf ("Memory Range: Reserved\n");
266	break;
267#endif
268    }
269
270    if (x & 0x20)
271	printf ("Memory Range: Memory is shadowable\n");
272    else
273	printf ("Memory Range: Memory is not shadowable\n");
274
275    if (x & 0x40)
276	printf ("Memory Range: Memory is an expansion ROM\n");
277    else
278	printf ("Memory Range: Memory is not an expansion ROM\n");
279
280#ifdef DIAGNOSTIC
281    if (x & 0x80)
282	printf ("Memory Range: Reserved (Device is brain-damaged)\n");
283#endif
284}
285
286
287/*
288 *  Small Resource Tag Handler
289 *
290 *  Returns 1 if checksum was valid (and an END_TAG was received).
291 *  Returns -1 if checksum was invalid (and an END_TAG was received).
292 *  Returns 0 for other tags.
293 */
294int
295handle_small_res(u_char *resinfo, int item, int len)
296{
297    int i;
298
299    DEB(printf("*** ITEM 0x%04x len %d detected\n", item, len));
300
301    switch (item) {
302    default:
303	printf("*** ITEM 0x%02x detected\n", item);
304	break;
305    case PNP_VERSION:
306	printf("PnP Version %d.%d, Vendor Version %d\n",
307	    resinfo[0] >> 4, resinfo[0] & (0xf), resinfo[1]);
308	break;
309    case LOG_DEVICE_ID:
310	printf("\nLogical Device ID: %c%c%c%02x%02x 0x%08x #%d\n",
311		((resinfo[0] & 0x7c) >> 2) + 64,
312		(((resinfo[0] & 0x03) << 3) |
313		((resinfo[1] & 0xe0) >> 5)) + 64,
314		(resinfo[1] & 0x1f) + 64,
315		resinfo[2], resinfo[3], *(int *)(resinfo),
316		logdevs++);
317
318	if (resinfo[4] & 0x1)
319	    printf ("\tDevice powers up active\n"); /* XXX */
320	if (resinfo[4] & 0x2)
321	    printf ("\tDevice supports I/O Range Check\n");
322	if (resinfo[4] > 0x3)
323	    printf ("\tReserved register funcs %02x\n",
324		resinfo[4]);
325
326	if (len == 6)
327	    printf("\tVendor register funcs %02x\n", resinfo[5]);
328	break;
329    case COMP_DEVICE_ID:
330	printf("Compatible Device ID: %c%c%c%02x%02x (%08x)\n",
331		((resinfo[0] & 0x7c) >> 2) + 64,
332		(((resinfo[0] & 0x03) << 3) |
333		((resinfo[1] & 0xe0) >> 5)) + 64,
334		(resinfo[1] & 0x1f) + 64,
335		resinfo[2], resinfo[3], *(int *)resinfo);
336	break;
337    case IRQ_FORMAT:
338	printf("    IRQ: ");
339
340	for (i = 0; i < 8; i++)
341	    if (resinfo[0] & (1<<i))
342		printf("%d ", i);
343	for (i = 0; i < 8; i++)
344	    if (resinfo[1] & (1<<i))
345		printf("%d ", i + 8);
346	if (len == 3) {
347	    if (resinfo[2] & 0x1)
348		printf("IRQ: High true edge sensitive\n");
349	    if (resinfo[2] & 0x2)
350		printf("IRQ: Low true edge sensitive\n");
351	    if (resinfo[2] & 0x4)
352		printf("IRQ: High true level sensitive\n");
353	    if (resinfo[2] & 0x8)
354		printf("IRQ: Low true level sensitive\n");
355	} else {
356	    printf(" - only one type (true/edge)\n");
357	}
358	break;
359    case DMA_FORMAT:
360	printf("    DMA: channel(s) ");
361	for (i = 0; i < 8; i++)
362	    if (resinfo[0] & (1<<i))
363		printf("%d ", i);
364	printf ("\n");
365	report_dma_info (resinfo[1]);
366	break;
367    case START_DEPEND_FUNC:
368	printf("TAG Start DF\n");
369	if (len == 1) {
370	    switch (resinfo[0]) {
371	    case 0:
372		printf("Good Configuration\n");
373		break;
374	    case 1:
375		printf("Acceptable Configuration\n");
376		break;
377	    case 2:
378		printf("Sub-optimal Configuration\n");
379		break;
380	    }
381	}
382	break;
383    case END_DEPEND_FUNC:
384	printf("TAG End DF\n");
385	break;
386    case IO_PORT_DESC:
387	printf("    I/O Range 0x%x .. 0x%x, alignment 0x%x, len 0x%x\n",
388	    resinfo[1] + (resinfo[2] << 8),
389	    resinfo[3] + (resinfo[4] << 8),
390	    resinfo[5], resinfo[6] );
391	if (resinfo[0])
392	    printf("\t[16-bit addr]\n");
393	else
394	    printf("\t[not 16-bit addr]\n");
395	break;
396    case FIXED_IO_PORT_DESC:
397	printf ("    FIXED I/O base address 0x%x length 0x%x\n",
398	    resinfo[0] + ( (resinfo[1] & 3 ) << 8), /* XXX */
399	    resinfo[2]);
400	break;
401#ifdef DIAGNOSTIC
402    case SM_RES_RESERVED:
403	printf("Reserved Tag Detected\n");
404	break;
405#endif
406    case SM_VENDOR_DEFINED:
407	printf("*** Small Vendor Tag Detected\n");
408	break;
409    case END_TAG:
410	printf("End Tag\n\n");
411	/* XXX Record and Verify Checksum */
412	return 1;
413	break;
414    }
415    return 0;
416}
417
418
419void
420handle_large_res(u_char *resinfo, int item, int len)
421{
422    int i;
423
424    DEB(printf("*** Large ITEM %d len %d found\n", item, len));
425    switch (item) {
426    case MEMORY_RANGE_DESC:
427	report_memory_info(resinfo[0]);
428	printf("Memory range minimum address: 0x%x\n",
429		(resinfo[1] << 8) + (resinfo[2] << 16));
430	printf("Memory range maximum address: 0x%x\n",
431		(resinfo[3] << 8) + (resinfo[4] << 16));
432	printf("Memory range base alignment: 0x%x\n",
433		(i = (resinfo[5] + (resinfo[6] << 8))) ? i : (1 << 16));
434	printf("Memory range length: 0x%x\n",
435		(resinfo[7] + (resinfo[8] << 8)) * 256);
436	break;
437    case ID_STRING_ANSI:
438	printf("Device Description: ");
439
440	for (i = 0; i < len; i++) {
441	    if (resinfo[i]) /* XXX */
442		printf("%c", resinfo[i]);
443	}
444	printf("\n");
445	break;
446    case ID_STRING_UNICODE:
447	printf("ID String Unicode Detected (Undefined)\n");
448	break;
449    case LG_VENDOR_DEFINED:
450	printf("Large Vendor Defined Detected\n");
451	break;
452    case _32BIT_MEM_RANGE_DESC:
453	printf("32bit Memory Range Desc Unimplemented\n");
454	break;
455    case _32BIT_FIXED_LOC_DESC:
456	printf("32bit Fixed Location Desc Unimplemented\n");
457	break;
458    case LG_RES_RESERVED:
459	printf("Large Reserved Tag Detected\n");
460	break;
461    }
462}
463
464
465/*
466 * Dump all the information about configurations.
467 */
468void
469dump_resdata(u_char *data, int csn)
470{
471    int i, large_len;
472
473    u_char tag, *resinfo;
474
475    DDB(printf("\nCard assigned CSN #%d\n", csn));
476    printf("Vendor ID %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
477	    ((data[0] & 0x7c) >> 2) + 64,
478	    (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
479	    (data[1] & 0x1f) + 64, data[2], data[3],
480	    *(int *)&(data[0]),
481	    *(int *)&(data[4]));
482
483    pnp_write(SET_CSN, csn); /* Move this out of this function XXX */
484    outb(_PNP_ADDRESS, STATUS);
485
486    /* Allows up to 1kb of Resource Info,  Should be plenty */
487    for (i = 0; i < 1024; i++) {
488	if (!get_resource_info(&tag, 1))
489	    break;
490
491#define TYPE	(tag >> 7)
492#define	S_ITEM	(tag >> 3)
493#define S_LEN	(tag & 0x7)
494#define	L_ITEM	(tag & 0x7f)
495
496	if (TYPE == 0) {
497	    /* Handle small resouce data types */
498
499	    resinfo = malloc(S_LEN);
500	    if (!get_resource_info(resinfo, S_LEN))
501		break;
502
503	    if (handle_small_res(resinfo, S_ITEM, S_LEN) == 1)
504		break;
505	    free(resinfo);
506	} else {
507	    /* Handle large resouce data types */
508
509	    if (!get_resource_info((char *) &large_len, 2))
510		break;
511
512	    resinfo = malloc(large_len);
513	    if (!get_resource_info(resinfo, large_len))
514		break;
515
516	    handle_large_res(resinfo, L_ITEM, 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(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(SET_LDN, i);
534
535	printf("\nLogical device #%d\n", pnp_read(SET_LDN) );
536	printf("IO: ");
537	for (j=0; j<8; j++)
538	    printf(" 0x%04x", pnp_readw(IO_CONFIG_BASE + j*2));
539	printf("\nIRQ %d %d\n",
540	    pnp_read(IRQ_CONFIG), pnp_read(IRQ_CONFIG+2) );
541	printf("DMA %d %d\n",
542	    pnp_read(DRQ_CONFIG), pnp_read(DRQ_CONFIG+1) );
543	printf("IO range check 0x%02x activate 0x%02x\n",
544	    pnp_read(IO_RANGE_CHECK), pnp_read(ACTIVATE) );
545    }
546}
547
548
549/*
550 * Run the isolation protocol. Use rd_port as the READ_DATA port
551 * value (caller should try multiple READ_DATA locations before giving
552 * up). Upon exiting, all cards are aware that they should use rd_port
553 * as the READ_DATA port;
554 *
555 */
556int
557isolation_protocol()
558{
559    int csn;
560    u_char data[9];
561
562    send_Initiation_LFSR();
563
564    /* Reset CSN for All Cards */
565    pnp_write(CONFIG_CONTROL, 0x04);
566
567    for (csn = 1; (csn < MAX_PNP_CARDS); csn++) {
568	/* Wake up cards without a CSN */
569	logdevs = 0 ;
570	pnp_write(WAKE, 0);
571	pnp_write(SET_RD_DATA, rd_port);
572	outb(_PNP_ADDRESS, SERIAL_ISOLATION);
573	DELAY(1000);	/* Delay 1 msec */
574
575	if (get_serial(data))
576	    dump_resdata(data, csn);
577	else
578	    break;
579    }
580    return csn - 1;
581}
582
583
584void
585main()
586{
587    int num_pnp_devs;
588
589    /* Hey what about a i386_iopl() call :) */
590    if (open("/dev/io", O_RDONLY) < 0) {
591	fprintf (stderr, "pnpinfo: Can't get I/O privilege.\n");
592	exit (1);
593    }
594    printf("Checking for Plug-n-Play devices...\n");
595
596    /* Try various READ_DATA ports from 0x203-0x3ff */
597    for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) {
598	DEB(printf("Trying Read_Port at %x...\n", (rd_port << 2) | 0x3) );
599	num_pnp_devs = isolation_protocol(rd_port);
600	if (num_pnp_devs)
601	    break;
602    }
603    if (!num_pnp_devs) {
604	printf("No Plug-n-Play devices were found\n");
605	return;
606    }
607}
608