#define DEBUG /* * Copyright (c) 1996, Sujal M. Patel * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ #include #include #include #include #include #include #include #include "pnpget.h" #define SEND(d, r) { outb (ADDRESS, d); outb (WRITE_DATA, r); } #define RECV(d, r) { outb (ADDRESS, d); r = inb((rd_port << 2) | 0x3); } /* The READ_DATA port that we are using currently */ static int rd_port; void DELAY __P((int i)); int power __P((int base, int exp)); void send_Initiation_LFSR(); int get_serial __P((unsigned char *data)); int get_resource_info __P((char *buffer, int len)); int handle_small_res __P((unsigned char *resinfo, int item, int len)); void handle_large_res __P((unsigned char *resinfo, int item, int len)); void dump_resdata __P((unsigned char *data, int csn)); int isolation_protocol(); /* * DELAY does accurate delaying in user-space. * This function busy-waits. */ void DELAY (i) int i; { struct timeval t; long start, stop; i *= 4; gettimeofday (&t, NULL); start = t.tv_sec * 1000000 + t.tv_usec; do { gettimeofday (&t, NULL); stop = t.tv_sec * 1000000 + t.tv_usec; } while (start + i > stop); } int power(base, exp) int base, exp; { if (exp <= 1) return base; else return base * power(base, exp - 1); } /* * Send Initiation LFSR as described in "Plug and Play ISA Specification, * Intel May 94." */ void send_Initiation_LFSR() { int cur, i; SEND (0x2, 0x2); /* Reset the LSFR */ outb(ADDRESS, 0); outb(ADDRESS, 0); cur = 0x6a; outb(ADDRESS, cur); for (i = 1; i < 32; i++) { cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); outb(ADDRESS, cur); } } /* * Get the device's serial number. Returns 1 if the serial is valid. */ int get_serial(data) unsigned char *data; { int i, bit, valid = 0, sum = 0x6a; bzero(data, sizeof(char) * 9); for (i = 0; i < 72; i++) { bit = inb((rd_port << 2) | 0x3) == 0x55; DELAY(250); /* Delay 250 usec */ /* Can't Short Circuit the next evaluation, so 'and' is last */ bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit; DELAY(250); /* Delay 250 usec */ valid = valid || bit; if (i < 64) sum = (sum >> 1) | (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); } valid = valid && (data[8] == sum); return valid; } void getinfo(data, csn) unsigned char *data; int csn; { struct cinfo *ci; int i, foo1, foo2; printf("Board Vendor ID: %c%c%c%02x%02x", ((data[0] & 0x7c) >> 2) + 64, (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64, (data[1] & 0x1f) + 64, data[2], data[3]); printf(" Board Serial Number: %08x\n", *(int *)&(data[4])); SEND(SET_CSN, csn); /* Move this out of this function XXX */ outb(ADDRESS, STATUS); for (i = 0; i < 8; i++) { RECV (IO_CONFIG_BASE + i * 2, foo1); RECV (IO_CONFIG_BASE + i * 2 + 1, foo2); printf ("%d: Base %x\n", i, (foo1 << 8) + foo2); } for (i = 0; i < 2; i++) { RECV (IRQ_CONFIG + i * 2, foo1); printf ("%d: IRQ %d\n", i, foo1); } for (i = 0; i < 2; i++) { RECV (DRQ_CONFIG + i, foo1); printf ("%d: DRQ %d\n", i, foo1); } #if 0 for (ci = cinfo; (int)ci < ((int)cinfo + sizeof (cinfo)); ci++) { if (ci->serial == *(int *)&(data[4])) { printf (" Configuring (Logical Device %x)\n", ci->ldn != -1 ? ci->ldn : 0); if (ci->ldn > 0) { SEND (SET_LDN, ci->ldn); } for (i = 0; i < 8; i++) if (ci->port[i] > 0) { SEND (IO_CONFIG_BASE + i * 2, ci->port[i] >> 8); SEND (IO_CONFIG_BASE + i * 2 + 1, ci->port[i] & 0xff); } for (i = 0; i < 2; i++) if (ci->irq[i].num > 0) { SEND (IRQ_CONFIG + i * 2, ci->irq[i].num); if (ci->irq[i].type >= 0) SEND (IRQ_CONFIG + i * 2 + 1, ci->irq[i].type); } for (i = 0; i < 2; i++) if (ci->drq[i] > 0) { SEND (DRQ_CONFIG + i, ci->drq[i]); } for (i = 0; i < 4; i++) if (ci->mem[i].base > 0) { SEND (MEM_CONFIG + i * 8, ci->mem[i].base >> 16); SEND (MEM_CONFIG + i * 8 + 1, (ci->mem[i].base >> 8) & 0xff); /* * This needs to be handled better for * the user's sake. XXX */ if (ci->mem[i].control >= 0) { SEND (MEM_CONFIG + i * 8 + 2, ci->mem[i].control); } SEND (MEM_CONFIG + i * 8 + 3, ci->mem[i].range >> 16); SEND (MEM_CONFIG + i * 8 + 4, (ci->mem[i].range >> 8) & 0xff); } SEND (IO_RANGE_CHECK, 0); SEND (ACTIVATE, 1); } } #endif } /* * Run the isolation protocol. Use rd_port as the READ_DATA port value (caller * should try multiple READ_DATA locations before giving up). Upon exiting, * all cards are aware that they should use rd_port as the READ_DATA port; */ int isolation_protocol() { int csn; unsigned char data[9]; send_Initiation_LFSR(); /* Reset CSN for All Cards */ SEND(0x02, 0x04); for (csn = 1; (csn < MAX_CARDS); csn++) { /* Wake up cards without a CSN */ SEND(WAKE, 0); SEND(SET_RD_DATA, rd_port); outb(ADDRESS, SERIAL_ISOLATION); DELAY(1000); /* Delay 1 msec */ if (get_serial(data)) getinfo(data, csn); else break; } return csn - 1; } void main() { int num_pnp_devs; /* Hey what about a i386_iopl() call :) */ if (open("/dev/io", O_RDONLY) < 0) { fprintf (stderr, "pnpinfo: Can't get I/O privilege.\n"); exit (1); } printf("Checking for Plug-n-Play devices...\n"); /* Try various READ_DATA ports from 0x203-0x3ff */ for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) { #ifdef DEBUG printf("Trying Read_Port at %x\n", (rd_port << 2) | 0x3); #endif num_pnp_devs = isolation_protocol(rd_port); if (num_pnp_devs) break; } if (!num_pnp_devs) { printf("No Plug-n-Play devices were found\n"); return; } }