pnpinfo.c revision 229236
11844Swollman/*
250476Speter * Copyright (c) 1996, Sujal M. Patel
31844Swollman * All rights reserved.
41638Srgrimes *
594940Sru * Redistribution and use in source and binary forms, with or without
61638Srgrimes * modification, are permitted provided that the following conditions
742915Sjdp * are met:
842915Sjdp * 1. Redistributions of source code must retain the above copyright
942915Sjdp *    notice, this list of conditions and the following disclaimer.
1042915Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1142915Sjdp *    notice, this list of conditions and the following disclaimer in the
1242915Sjdp *    documentation and/or other materials provided with the distribution.
1342915Sjdp *
1442915Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1599362Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1642915Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1729141Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18100375Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19100332Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20100332Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2142915Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2242915Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2329141Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24117034Sgordon * SUCH DAMAGE.
25117034Sgordon */
262827Sjkh
272827Sjkh#include <sys/cdefs.h>
282827Sjkh__FBSDID("$FreeBSD: stable/9/contrib/pnpinfo/pnpinfo.c 229236 2012-01-01 23:26:49Z dim $");
292827Sjkh
302827Sjkh#include <sys/time.h>
311638Srgrimes
322827Sjkh#include <err.h>
331638Srgrimes#include <stdio.h>
3418529Sbde#include <stdlib.h>
3518529Sbde#include <unistd.h>
361638Srgrimes#include <fcntl.h>
3742450Sjdp#include <string.h>
381638Srgrimes
3995064Sobrien#include <machine/cpufunc.h>
401638Srgrimes
4196512Sru#include <isa/pnpreg.h>
4296512Sru
4396512Sru#ifdef DEBUG
4496512Sru#define	DEB(x) x
4596512Sru#else
4696512Sru#define DEB(x)
4796512Sru#endif
4896512Sru#define DDB(x) x
4992491Smarkm
5092491Smarkmvoid
5192553Srupnp_write(int d, u_char r)
5292491Smarkm{
5392491Smarkm    outb (_PNP_ADDRESS, d);
5492553Sru    outb (_PNP_WRITE_DATA, r);
5592553Sru}
5692491Smarkm
571638Srgrimes/* The READ_DATA port that we are using currently */
581844Swollmanstatic int rd_port;
59116855Speter
6038186Speteru_char
611638Srgrimespnp_read(int d)
621638Srgrimes{
6324761Sjdp    outb(_PNP_ADDRESS, d);
64116855Speter    return inb( (rd_port << 2) + 3) & 0xff;
6538186Speter}
661638Srgrimes
6742450Sjdpu_short
681844Swollmanpnp_readw(int d)
6996258Sobrien{
7038186Speter    int c = pnp_read(d) << 8 ;
711844Swollman    c |= pnp_read(d+1);
7236673Sdt    return c;
731844Swollman}
74116855Speter
7538186Speterint logdevs=0;
761844Swollman
7736673Sdtvoid DELAY __P((int i));
7824761Sjdpvoid send_Initiation_LFSR();
79116855Speterint get_serial __P((u_char *data));
8038186Speterint get_resource_info __P((u_char *buffer, int len));
811844Swollmanint handle_small_res __P((u_char *resinfo, int item, int len));
8242450Sjdpvoid handle_large_res __P((u_char *resinfo, int item, int len));
831844Swollmanvoid dump_resdata __P((u_char *data, int csn));
8496258Sobrienint isolation_protocol();
8538186Speter
861844Swollman
871844Swollman/*
881844Swollman * DELAY does accurate delaying in user-space.
89116855Speter * This function busy-waits.
9038186Speter */
911844Swollmanvoid
921844SwollmanDELAY (int i)
9324761Sjdp{
94116855Speter    struct timeval t;
9538186Speter    long start, stop;
961844Swollman
9742450Sjdp    i *= 4;
981844Swollman
9996258Sobrien    gettimeofday (&t, NULL);
10038186Speter    start = t.tv_sec * 1000000 + t.tv_usec;
1011844Swollman    do {
10236054Sbde	gettimeofday (&t, NULL);
10336054Sbde	stop = t.tv_sec * 1000000 + t.tv_usec;
104116855Speter    } while (start + i > stop);
10538186Speter}
10636054Sbde
10736054Sbde
10836054Sbde/*
109116855Speter * Send Initiation LFSR as described in "Plug and Play ISA Specification,
11038186Speter * Intel May 94."
11136054Sbde */
11242450Sjdpvoid
11336054Sbdesend_Initiation_LFSR()
11496258Sobrien{
11538186Speter    int cur, i;
11636054Sbde
11795251Sru    pnp_write(PNP_CONFIG_CONTROL, 0x2);
11897101Sru
11917510Speter    /* Reset the LSFR */
12096258Sobrien    outb(_PNP_ADDRESS, 0);
12138186Speter    outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
1221638Srgrimes
12395251Sru    cur = 0x6a;
12497101Sru
12517510Speter    for (i = 0; i < 32; i++) {
12696258Sobrien	outb(_PNP_ADDRESS, cur);
12738186Speter	cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
1281638Srgrimes    }
12995251Sru}
13097101Sru
13195216Smarkm/*
132116855Speter * Get the device's serial number.  Returns 1 if the serial is valid.
13338186Speter */
1341638Srgrimesint
1351844Swollmanget_serial(u_char *data)
13697101Sru{
13796258Sobrien    int i, bit, valid = 0, sum = 0x6a;
13838186Speter
1391844Swollman    bzero(data, sizeof(char) * 9);
1401844Swollman
14197101Sru    for (i = 0; i < 72; i++) {
14296258Sobrien	bit = inb((rd_port << 2) | 0x3) == 0x55;
14338186Speter	DELAY(250);	/* Delay 250 usec */
1441844Swollman
14542450Sjdp	/* Can't Short Circuit the next evaluation, so 'and' is last */
14697101Sru	bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit;
14795216Smarkm	DELAY(250);	/* Delay 250 usec */
148116855Speter
14938186Speter	valid = valid || bit;
1501844Swollman
15196512Sru	if (i < 64)
1521638Srgrimes	    sum = (sum >> 1) |
15399362Sru		(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
15499362Sru
15599362Sru	data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
15699362Sru    }
15796512Sru
15896512Sru    valid = valid && (data[8] == sum);
1591638Srgrimes
16096512Sru    return valid;
16196512Sru}
16296512Sru
16396512Sru
16496512Sru/*
16599362Sru * Fill's the buffer with resource info from the device.
1661638Srgrimes * Returns 0 if the device fails to report
16796512Sru */
16895114Sobrienint
16999362Sruget_resource_info(u_char *buffer, int len)
17096512Sru{
17196512Sru    int i, j;
17295306Sru
17396512Sru    for (i = 0; i < len; i++) {
17496512Sru	outb(_PNP_ADDRESS, PNP_STATUS);
17596512Sru	for (j = 0; j < 100; j++) {
17696512Sru	    if ((inb((rd_port << 2) | 0x3)) & 0x1)
17796512Sru		break;
17874805Sru	    DELAY(1);
1791844Swollman	}
18099362Sru	if (j == 100) {
18199362Sru	    printf("PnP device failed to report resource data\n");
18296512Sru	    return 0;
18399362Sru	}
1841844Swollman	outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
18596512Sru	buffer[i] = inb((rd_port << 2) | 0x3);
18696512Sru	DEB(printf("--- get_resource_info: got 0x%02x\n",(unsigned)buffer[i]));
1871638Srgrimes    }
18842915Sjdp    return 1;
18942915Sjdp}
19096512Sru
19142915Sjdpvoid
19296512Srureport_dma_info (x)
19342915Sjdp	int x;
19496343Sobrien{
19596512Sru    char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
19691011Sru
19728945Speter    switch (x & 0x3) {
1981844Swollman    case 0:
19999362Sru	s1="8-bit";
20096512Sru	break;
20196512Sru    case 1:
20296512Sru	s1="8/16-bit";
2032353Sbde	break;
20496512Sru    case 2:
20596512Sru	s1="16-bit";
20696512Sru	break;
2073859Sbde#ifdef DIAGNOSTIC
2081844Swollman    case 3:
209103713Smarkm	s1="Reserved";
21096512Sru	break;
21196512Sru#endif
21296512Sru    }
21396512Sru
21492491Smarkm    s2 = (x & 0x4) ? "bus master" : "not a bus master";
21596512Sru
21696512Sru    s3 = (x & 0x8) ? "count by byte" : "";
21792491Smarkm
21892491Smarkm    s4 = (x & 0x10) ? "count by word" : "";
2191638Srgrimes
22096512Sru    switch ((x & 0x60) >> 5) {
22196512Sru    case 0:
22296512Sru	s5="Compatibility mode";
22396512Sru	break;
22496512Sru    case 1:
22596512Sru	s5="Type A";
2261638Srgrimes	break;
2271638Srgrimes    case 2:
22834179Sbde	s5="Type B";
22924750Sbde	break;
23042450Sjdp    case 3:
23124750Sbde	s5="Type F";
23224750Sbde	break;
23342915Sjdp    }
23431809Sbde    printf("\t%s, %s, %s, %s, %s\n",s1,s2,s3,s4,s5);
23542915Sjdp}
23627910Sasami
23728945Speter
2381638Srgrimesvoid
2391638Srgrimesreport_memory_info (int x)
2401638Srgrimes{
24148204Sjmg    if (x & 0x1)
2422298Swollman	printf ("Memory Range: Writeable\n");
2432298Swollman    else
2442298Swollman	printf ("Memory Range: Not writeable (ROM)\n");
24549328Shoek
24649328Shoek    if (x & 0x2)
24749328Shoek	printf ("Memory Range: Read-cacheable, write-through\n");
24849328Shoek    else
24956971Sru	printf ("Memory Range: Non-cacheable\n");
25049328Shoek
25149328Shoek    if (x & 0x4)
25249328Shoek	printf ("Memory Range: Decode supports high address\n");
25349328Shoek    else
25499362Sru	printf ("Memory Range: Decode supports range length\n");
25595306Sru
25699343Sru    switch ((x & 0x18) >> 3) {
25795306Sru    case 0:
25899362Sru	printf ("Memory Range: 8-bit memory only\n");
25992980Sdes	break;
26049328Shoek    case 1:
26196512Sru	printf ("Memory Range: 16-bit memory only\n");
26299362Sru	break;
26392980Sdes    case 2:
26449328Shoek	printf ("Memory Range: 8-bit and 16-bit memory supported\n");
2651638Srgrimes	break;
266116144Sobrien#ifdef DIAGNOSTIC
267100872Sru    case 3:
26849328Shoek	printf ("Memory Range: Reserved\n");
26942915Sjdp	break;
27042915Sjdp#endif
27196512Sru    }
2721844Swollman
27328945Speter    if (x & 0x20)
27499362Sru	printf ("Memory Range: Memory is shadowable\n");
275100872Sru    else
27649328Shoek	printf ("Memory Range: Memory is not shadowable\n");
2771844Swollman
278103713Smarkm    if (x & 0x40)
279100872Sru	printf ("Memory Range: Memory is an expansion ROM\n");
28096462Sru    else
28196462Sru	printf ("Memory Range: Memory is not an expansion ROM\n");
28299362Sru
28396462Sru#ifdef DIAGNOSTIC
28497769Sru    if (x & 0x80)
28596668Sru	printf ("Memory Range: Reserved (Device is brain-damaged)\n");
28699256Sru#endif
28796462Sru}
28896162Sru
28996164Sru
29099343Sru/*
29196162Sru *  Small Resource Tag Handler
29296162Sru *
2931638Srgrimes *  Returns 1 if checksum was valid (and an END_TAG was received).
2941638Srgrimes *  Returns -1 if checksum was invalid (and an END_TAG was received).
2951638Srgrimes *  Returns 0 for other tags.
29695306Sru */
297103713Smarkmint
2981638Srgrimeshandle_small_res(u_char *resinfo, int item, int len)
2991638Srgrimes{
3001844Swollman    int i;
3011638Srgrimes
30274842Sru    DEB(printf("*** ITEM 0x%04x len %d detected\n", item, len));
3031844Swollman
3041844Swollman    switch (item) {
30534092Sbde    default:
30699362Sru	printf("*** ITEM 0x%02x detected\n", item);
30796512Sru	break;
30899362Sru    case PNP_TAG_VERSION:
30934092Sbde	printf("PnP Version %d.%d, Vendor Version %d\n",
31099362Sru	    resinfo[0] >> 4, resinfo[0] & (0xf), resinfo[1]);
31199362Sru	break;
31299362Sru    case PNP_TAG_LOGICAL_DEVICE:
31396512Sru	printf("\nLogical Device ID: %c%c%c%02x%02x 0x%08x #%d\n",
31499362Sru		((resinfo[0] & 0x7c) >> 2) + 64,
31534092Sbde		(((resinfo[0] & 0x03) << 3) |
316100457Sru		((resinfo[1] & 0xe0) >> 5)) + 64,
317100457Sru		(resinfo[1] & 0x1f) + 64,
318100457Sru		resinfo[2], resinfo[3], *(int *)(resinfo),
319100457Sru		logdevs++);
320100457Sru
321100457Sru	if (resinfo[4] & 0x1)
322100457Sru	    printf ("\tDevice powers up active\n"); /* XXX */
323100457Sru	if (resinfo[4] & 0x2)
324100457Sru	    printf ("\tDevice supports I/O Range Check\n");
325100457Sru	if (resinfo[4] > 0x3)
326100457Sru	    printf ("\tReserved register funcs %02x\n",
327100457Sru		resinfo[4]);
328100457Sru
329100457Sru	if (len == 6)
330100457Sru	    printf("\tVendor register funcs %02x\n", resinfo[5]);
331100457Sru	break;
332100457Sru    case PNP_TAG_COMPAT_DEVICE:
333100457Sru	printf("Compatible Device ID: %c%c%c%02x%02x (%08x)\n",
334100457Sru		((resinfo[0] & 0x7c) >> 2) + 64,
335100457Sru		(((resinfo[0] & 0x03) << 3) |
336100457Sru		((resinfo[1] & 0xe0) >> 5)) + 64,
337100457Sru		(resinfo[1] & 0x1f) + 64,
338100457Sru		resinfo[2], resinfo[3], *(int *)resinfo);
339100457Sru	break;
340100457Sru    case PNP_TAG_IRQ_FORMAT:
341100457Sru	printf("    IRQ: ");
342100457Sru
343100457Sru	for (i = 0; i < 8; i++)
344100457Sru	    if (resinfo[0] & (1<<i))
345100457Sru		printf("%d ", i);
346100457Sru	for (i = 0; i < 8; i++)
347100457Sru	    if (resinfo[1] & (1<<i))
348100457Sru		printf("%d ", i + 8);
349100457Sru	if (len == 3) {
350100457Sru	    if (resinfo[2] & 0x1)
351100457Sru		printf("IRQ: High true edge sensitive\n");
35216663Sjkh	    if (resinfo[2] & 0x2)
35376861Skris		printf("IRQ: Low true edge sensitive\n");
35476861Skris	    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
422void
423handle_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 */
473void
474dump_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 */
558int
559isolation_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
586int
587main(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}
612