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