1999-09-01 20:53:43 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* $FreeBSD$
|
|
|
|
* from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <isa/isavar.h>
|
|
|
|
#include <isa/pnpreg.h>
|
|
|
|
#include <isa/pnpvar.h>
|
2000-08-28 21:48:13 +00:00
|
|
|
#include <machine/bus.h>
|
1999-09-01 20:53:43 +00:00
|
|
|
|
|
|
|
typedef struct _pnp_id {
|
|
|
|
u_int32_t vendor_id;
|
|
|
|
u_int32_t serial;
|
|
|
|
u_char checksum;
|
|
|
|
} pnp_id;
|
|
|
|
|
|
|
|
struct pnp_set_config_arg {
|
|
|
|
int csn; /* Card number to configure */
|
|
|
|
int ldn; /* Logical device on card */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pnp_quirk {
|
|
|
|
u_int32_t vendor_id; /* Vendor of the card */
|
|
|
|
u_int32_t logical_id; /* ID of the device with quirk */
|
|
|
|
int type;
|
|
|
|
#define PNP_QUIRK_WRITE_REG 1 /* Need to write a pnp register */
|
2000-07-11 11:49:33 +00:00
|
|
|
#define PNP_QUIRK_EXTRA_IO 2 /* Has extra io ports */
|
1999-09-01 20:53:43 +00:00
|
|
|
int arg1;
|
|
|
|
int arg2;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pnp_quirk pnp_quirks[] = {
|
|
|
|
/*
|
|
|
|
* The Gravis UltraSound needs register 0xf2 to be set to 0xff
|
|
|
|
* to enable power.
|
|
|
|
* XXX need to know the logical device id.
|
|
|
|
*/
|
|
|
|
{ 0x0100561e /* GRV0001 */, 0,
|
|
|
|
PNP_QUIRK_WRITE_REG, 0xf2, 0xff },
|
2000-07-11 11:49:33 +00:00
|
|
|
/*
|
|
|
|
* An emu8000 does not give us other than the first
|
|
|
|
* port.
|
|
|
|
*/
|
|
|
|
{ 0x26008c0e /* SB16 */, 0x21008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
|
|
|
{ 0x42008c0e /* SB32(CTL0042) */, 0x21008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
|
|
|
{ 0x44008c0e /* SB32(CTL0044) */, 0x21008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
|
|
|
{ 0x49008c0e /* SB32(CTL0049) */, 0x21008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
|
|
|
{ 0xf1008c0e /* SB32(CTL00f1) */, 0x21008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
|
|
|
{ 0xc1008c0e /* SB64(CTL00c1) */, 0x22008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
2002-02-05 06:52:56 +00:00
|
|
|
{ 0xc5008c0e /* SB64(CTL00c5) */, 0x22008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
2000-07-11 11:49:33 +00:00
|
|
|
{ 0xe4008c0e /* SB64(CTL00e4) */, 0x22008c0e,
|
|
|
|
PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
|
1999-09-01 20:53:43 +00:00
|
|
|
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
2002-09-29 13:31:26 +00:00
|
|
|
#ifdef PC98
|
|
|
|
/* Some NEC PnP cards have 9 bytes serial code. */
|
|
|
|
static pnp_id necids[] = {
|
|
|
|
{0x4180a3b8, 0xffffffff, 0x00}, /* PC-9801CB-B04 (NEC8041) */
|
|
|
|
{0x5181a3b8, 0xffffffff, 0x46}, /* PC-9821CB2-B04(NEC8151) */
|
|
|
|
{0x5182a3b8, 0xffffffff, 0xb8}, /* PC-9801-XX (NEC8251) */
|
|
|
|
{0x9181a3b8, 0xffffffff, 0x00}, /* PC-9801-120 (NEC8191) */
|
|
|
|
{0, 0, 0}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* these entries are initialized using the autoconfig menu
|
|
|
|
* The struct is invalid (and must be initialized) if the first
|
|
|
|
* CSN is zero. The init code fills invalid entries with CSN 255
|
|
|
|
* which is not a supported value.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = {
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The READ_DATA port that we are using currently */
|
|
|
|
static int pnp_rd_port;
|
|
|
|
|
|
|
|
static void pnp_send_initiation_key(void);
|
|
|
|
static int pnp_get_serial(pnp_id *p);
|
|
|
|
static int pnp_isolation_protocol(device_t parent);
|
|
|
|
|
1999-10-14 21:03:03 +00:00
|
|
|
char *
|
|
|
|
pnp_eisaformat(u_int32_t id)
|
|
|
|
{
|
|
|
|
u_int8_t *data = (u_int8_t *) &id;
|
|
|
|
static char idbuf[8];
|
|
|
|
const char hextoascii[] = "0123456789abcdef";
|
|
|
|
|
|
|
|
idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
|
|
|
|
idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
|
|
|
|
idbuf[2] = '@' + (data[1] & 0x1f);
|
|
|
|
idbuf[3] = hextoascii[(data[2] >> 4)];
|
|
|
|
idbuf[4] = hextoascii[(data[2] & 0xf)];
|
|
|
|
idbuf[5] = hextoascii[(data[3] >> 4)];
|
|
|
|
idbuf[6] = hextoascii[(data[3] & 0xf)];
|
|
|
|
idbuf[7] = 0;
|
|
|
|
return(idbuf);
|
|
|
|
}
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
static void
|
|
|
|
pnp_write(int d, u_char r)
|
|
|
|
{
|
|
|
|
outb (_PNP_ADDRESS, d);
|
|
|
|
outb (_PNP_WRITE_DATA, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
static u_char
|
|
|
|
pnp_read(int d)
|
|
|
|
{
|
|
|
|
outb (_PNP_ADDRESS, d);
|
|
|
|
return (inb(3 | (pnp_rd_port <<2)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send Initiation LFSR as described in "Plug and Play ISA Specification",
|
|
|
|
* Intel May 94.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pnp_send_initiation_key()
|
|
|
|
{
|
|
|
|
int cur, i;
|
|
|
|
|
|
|
|
/* Reset the LSFR */
|
|
|
|
outb(_PNP_ADDRESS, 0);
|
|
|
|
outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
|
|
|
|
|
|
|
|
cur = 0x6a;
|
|
|
|
outb(_PNP_ADDRESS, cur);
|
|
|
|
|
|
|
|
for (i = 1; i < 32; i++) {
|
|
|
|
cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
|
|
|
|
outb(_PNP_ADDRESS, cur);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the device's serial number. Returns 1 if the serial is valid.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pnp_get_serial(pnp_id *p)
|
|
|
|
{
|
|
|
|
int i, bit, valid = 0, sum = 0x6a;
|
|
|
|
u_char *data = (u_char *)p;
|
|
|
|
|
|
|
|
bzero(data, sizeof(char) * 9);
|
|
|
|
outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
|
|
|
|
for (i = 0; i < 72; i++) {
|
|
|
|
bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
|
|
|
|
DELAY(250); /* Delay 250 usec */
|
|
|
|
|
|
|
|
/* Can't Short Circuit the next evaluation, so 'and' is last */
|
|
|
|
bit = (inb((pnp_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill's the buffer with resource info from the device.
|
1999-10-09 13:11:46 +00:00
|
|
|
* Returns the number of characters read.
|
1999-09-01 20:53:43 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pnp_get_resource_info(u_char *buffer, int len)
|
|
|
|
{
|
1999-10-09 13:11:46 +00:00
|
|
|
int i, j, count;
|
1999-09-01 20:53:43 +00:00
|
|
|
u_char temp;
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
count = 0;
|
1999-09-01 20:53:43 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
outb(_PNP_ADDRESS, PNP_STATUS);
|
|
|
|
for (j = 0; j < 100; j++) {
|
|
|
|
if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1)
|
|
|
|
break;
|
|
|
|
DELAY(1);
|
|
|
|
}
|
|
|
|
if (j == 100) {
|
|
|
|
printf("PnP device failed to report resource data\n");
|
1999-10-09 13:11:46 +00:00
|
|
|
return count;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
|
|
|
|
temp = inb((pnp_rd_port << 2) | 0x3);
|
|
|
|
if (buffer != NULL)
|
|
|
|
buffer[i] = temp;
|
1999-10-09 13:11:46 +00:00
|
|
|
count++;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
return count;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* write_pnp_parms initializes a logical device with the parms
|
|
|
|
* in d, and then activates the board if the last parameter is 1.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
write_pnp_parms(struct pnp_cinfo *d, pnp_id *p, int ldn)
|
|
|
|
{
|
|
|
|
int i, empty = -1 ;
|
|
|
|
|
|
|
|
pnp_write (SET_LDN, ldn );
|
|
|
|
i = pnp_read(SET_LDN) ;
|
|
|
|
if (i != ldn) {
|
|
|
|
printf("Warning: LDN %d does not exist\n", ldn);
|
|
|
|
}
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
pnp_write(IO_CONFIG_BASE + i * 2, d->ic_port[i] >> 8 );
|
|
|
|
pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->ic_port[i] & 0xff );
|
|
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
pnp_write(MEM_CONFIG + i*8, (d->ic_mem[i].base >> 16) & 0xff );
|
|
|
|
pnp_write(MEM_CONFIG + i*8+1, (d->ic_mem[i].base >> 8) & 0xff );
|
|
|
|
pnp_write(MEM_CONFIG + i*8+2, d->ic_mem[i].control & 0xff );
|
|
|
|
pnp_write(MEM_CONFIG + i*8+3, (d->ic_mem[i].range >> 16) & 0xff );
|
|
|
|
pnp_write(MEM_CONFIG + i*8+4, (d->ic_mem[i].range >> 8) & 0xff );
|
|
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
pnp_write(IRQ_CONFIG + i*2 , d->irq[i] );
|
|
|
|
pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] );
|
|
|
|
pnp_write(DRQ_CONFIG + i, d->drq[i] );
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* store parameters read into the current kernel
|
|
|
|
* so manual editing next time is easier
|
|
|
|
*/
|
|
|
|
for (i = 0 ; i < MAX_PNP_LDN; i++) {
|
|
|
|
if (pnp_ldn_overrides[i].csn == d->csn &&
|
|
|
|
pnp_ldn_overrides[i].ldn == ldn) {
|
|
|
|
d->flags = pnp_ldn_overrides[i].flags ;
|
|
|
|
pnp_ldn_overrides[i] = *d ;
|
|
|
|
break ;
|
|
|
|
} else if (pnp_ldn_overrides[i].csn < 1 ||
|
|
|
|
pnp_ldn_overrides[i].csn == 255)
|
|
|
|
empty = i ;
|
|
|
|
}
|
|
|
|
if (i== MAX_PNP_LDN && empty != -1)
|
|
|
|
pnp_ldn_overrides[empty] = *d;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here should really perform the range check, and
|
|
|
|
* return a failure if not successful.
|
|
|
|
*/
|
|
|
|
pnp_write (IO_RANGE_CHECK, 0);
|
|
|
|
DELAY(1000); /* XXX is it really necessary ? */
|
|
|
|
pnp_write (ACTIVATE, d->enable ? 1 : 0);
|
|
|
|
DELAY(1000); /* XXX is it really necessary ? */
|
|
|
|
return 1 ;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is called after the bus has assigned resource
|
|
|
|
* locations for a logical device.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pnp_set_config(void *arg, struct isa_config *config, int enable)
|
|
|
|
{
|
|
|
|
int csn = ((struct pnp_set_config_arg *) arg)->csn;
|
|
|
|
int ldn = ((struct pnp_set_config_arg *) arg)->ldn;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First put all cards into Sleep state with the initiation
|
|
|
|
* key, then put our card into Config state.
|
|
|
|
*/
|
|
|
|
pnp_send_initiation_key();
|
|
|
|
pnp_write(PNP_WAKE, csn);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select our logical device so that we can program it.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_SET_LDN, ldn);
|
|
|
|
|
2000-10-09 00:40:17 +00:00
|
|
|
/*
|
|
|
|
* Constrain the number of resources we will try to program
|
|
|
|
*/
|
|
|
|
if (config->ic_nmem > ISA_PNP_NMEM) {
|
|
|
|
printf("too many ISA memory ranges (%d > %d)\n", config->ic_nmem, ISA_PNP_NMEM);
|
|
|
|
config->ic_nmem = ISA_PNP_NMEM;
|
|
|
|
}
|
|
|
|
if (config->ic_nport > ISA_PNP_NPORT) {
|
|
|
|
printf("too many ISA I/O ranges (%d > %d)\n", config->ic_nport, ISA_PNP_NPORT);
|
|
|
|
config->ic_nport = ISA_PNP_NPORT;
|
|
|
|
}
|
|
|
|
if (config->ic_nirq > ISA_PNP_NIRQ) {
|
|
|
|
printf("too many ISA IRQs (%d > %d)\n", config->ic_nirq, ISA_PNP_NIRQ);
|
|
|
|
config->ic_nirq = ISA_PNP_NIRQ;
|
|
|
|
}
|
|
|
|
if (config->ic_ndrq > ISA_PNP_NDRQ) {
|
|
|
|
printf("too many ISA DRQs (%d > %d)\n", config->ic_ndrq, ISA_PNP_NDRQ);
|
|
|
|
config->ic_ndrq = ISA_PNP_NDRQ;
|
|
|
|
}
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
/*
|
|
|
|
* Now program the resources.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < config->ic_nmem; i++) {
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
u_int32_t start;
|
|
|
|
u_int32_t size;
|
|
|
|
|
|
|
|
/* XXX: should handle memory control register, 32 bit memory */
|
|
|
|
if (config->ic_mem[i].ir_size == 0) {
|
|
|
|
pnp_write(PNP_MEM_BASE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_MEM_BASE_LOW(i), 0);
|
|
|
|
pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_MEM_RANGE_LOW(i), 0);
|
|
|
|
} else {
|
|
|
|
start = config->ic_mem[i].ir_start;
|
|
|
|
size = config->ic_mem[i].ir_size;
|
|
|
|
if (start & 0xff)
|
|
|
|
panic("pnp_set_config: bogus memory assignment");
|
|
|
|
pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
|
|
|
|
pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
|
|
|
|
pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
|
|
|
|
pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
|
|
|
|
}
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
2000-10-09 00:40:17 +00:00
|
|
|
for (; i < ISA_PNP_NMEM; i++) {
|
1999-09-01 20:53:43 +00:00
|
|
|
pnp_write(PNP_MEM_BASE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_MEM_BASE_LOW(i), 0);
|
|
|
|
pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_MEM_RANGE_LOW(i), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < config->ic_nport; i++) {
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
u_int32_t start;
|
|
|
|
|
|
|
|
if (config->ic_port[i].ir_size == 0) {
|
|
|
|
pnp_write(PNP_IO_BASE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_IO_BASE_LOW(i), 0);
|
|
|
|
} else {
|
|
|
|
start = config->ic_port[i].ir_start;
|
|
|
|
pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
|
|
|
|
pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
|
|
|
|
}
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
2000-10-09 00:40:17 +00:00
|
|
|
for (; i < ISA_PNP_NPORT; i++) {
|
1999-09-01 20:53:43 +00:00
|
|
|
pnp_write(PNP_IO_BASE_HIGH(i), 0);
|
|
|
|
pnp_write(PNP_IO_BASE_LOW(i), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < config->ic_nirq; i++) {
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
int irq;
|
|
|
|
|
|
|
|
/* XXX: interrupt type */
|
|
|
|
if (config->ic_irqmask[i] == 0) {
|
|
|
|
pnp_write(PNP_IRQ_LEVEL(i), 0);
|
|
|
|
pnp_write(PNP_IRQ_TYPE(i), 2);
|
|
|
|
} else {
|
|
|
|
irq = ffs(config->ic_irqmask[i]) - 1;
|
|
|
|
pnp_write(PNP_IRQ_LEVEL(i), irq);
|
|
|
|
pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
|
|
|
|
}
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
2000-10-09 00:40:17 +00:00
|
|
|
for (; i < ISA_PNP_NIRQ; i++) {
|
1999-09-01 20:53:43 +00:00
|
|
|
/*
|
|
|
|
* IRQ 0 is not a valid interrupt selection and
|
|
|
|
* represents no interrupt selection.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_IRQ_LEVEL(i), 0);
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
pnp_write(PNP_IRQ_TYPE(i), 2);
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < config->ic_ndrq; i++) {
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
int drq;
|
|
|
|
|
|
|
|
if (config->ic_drqmask[i] == 0) {
|
|
|
|
pnp_write(PNP_DMA_CHANNEL(i), 4);
|
|
|
|
} else {
|
|
|
|
drq = ffs(config->ic_drqmask[i]) - 1;
|
|
|
|
pnp_write(PNP_DMA_CHANNEL(i), drq);
|
|
|
|
}
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
2000-10-09 00:40:17 +00:00
|
|
|
for (; i < ISA_PNP_NDRQ; i++) {
|
1999-09-01 20:53:43 +00:00
|
|
|
/*
|
|
|
|
* DMA channel 4, the cascade channel is used to
|
|
|
|
* indicate no DMA channel is active.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_DMA_CHANNEL(i), 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
pnp_write(PNP_ACTIVATE, enable ? 1 : 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wake everyone up again, we are finished.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process quirks for a logical device.. The card must be in Config state.
|
|
|
|
*/
|
2000-07-11 11:49:33 +00:00
|
|
|
void
|
|
|
|
pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config)
|
1999-09-01 20:53:43 +00:00
|
|
|
{
|
|
|
|
struct pnp_quirk *qp;
|
|
|
|
|
|
|
|
for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) {
|
|
|
|
if (qp->vendor_id == vendor_id
|
|
|
|
&& (qp->logical_id == 0
|
|
|
|
|| qp->logical_id == logical_id)) {
|
|
|
|
switch (qp->type) {
|
|
|
|
case PNP_QUIRK_WRITE_REG:
|
|
|
|
pnp_write(PNP_SET_LDN, ldn);
|
|
|
|
pnp_write(qp->arg1, qp->arg2);
|
|
|
|
break;
|
2000-07-11 11:49:33 +00:00
|
|
|
case PNP_QUIRK_EXTRA_IO:
|
|
|
|
if (config == NULL)
|
|
|
|
break;
|
|
|
|
if (qp->arg1 != 0) {
|
|
|
|
config->ic_nport++;
|
|
|
|
config->ic_port[config->ic_nport - 1] = config->ic_port[0];
|
|
|
|
config->ic_port[config->ic_nport - 1].ir_start += qp->arg1;
|
|
|
|
config->ic_port[config->ic_nport - 1].ir_end += qp->arg1;
|
|
|
|
}
|
|
|
|
if (qp->arg2 != 0) {
|
|
|
|
config->ic_nport++;
|
|
|
|
config->ic_port[config->ic_nport - 1] = config->ic_port[0];
|
|
|
|
config->ic_port[config->ic_nport - 1].ir_start += qp->arg2;
|
|
|
|
config->ic_port[config->ic_nport - 1].ir_end += qp->arg2;
|
|
|
|
}
|
|
|
|
break;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan Resource Data for Logical Devices.
|
|
|
|
*
|
|
|
|
* This function exits as soon as it gets an error reading *ANY*
|
1999-10-09 13:11:46 +00:00
|
|
|
* Resource Data or it reaches the end of Resource Data. In the first
|
1999-09-01 20:53:43 +00:00
|
|
|
* case the return value will be TRUE, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
1999-10-09 13:11:46 +00:00
|
|
|
pnp_create_devices(device_t parent, pnp_id *p, int csn,
|
|
|
|
u_char *resources, int len)
|
1999-09-01 20:53:43 +00:00
|
|
|
{
|
1999-10-09 13:11:46 +00:00
|
|
|
u_char tag, *resp, *resinfo, *startres = 0;
|
|
|
|
int large_len, scanning = len, retval = FALSE;
|
1999-09-01 20:53:43 +00:00
|
|
|
u_int32_t logical_id;
|
|
|
|
device_t dev = 0;
|
|
|
|
int ldn = 0;
|
|
|
|
struct pnp_set_config_arg *csnldn;
|
1999-10-09 13:11:46 +00:00
|
|
|
char buf[100];
|
1999-09-01 20:53:43 +00:00
|
|
|
char *desc = 0;
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
resp = resources;
|
|
|
|
while (scanning > 0) {
|
|
|
|
tag = *resp++;
|
|
|
|
scanning--;
|
|
|
|
if (PNP_RES_TYPE(tag) != 0) {
|
|
|
|
/* Large resource */
|
|
|
|
if (scanning < 2) {
|
1999-09-01 20:53:43 +00:00
|
|
|
scanning = 0;
|
|
|
|
continue;
|
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
large_len = resp[0] + (resp[1] << 8);
|
|
|
|
resp += 2;
|
1999-09-01 20:53:43 +00:00
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
if (scanning < large_len) {
|
1999-09-01 20:53:43 +00:00
|
|
|
scanning = 0;
|
|
|
|
continue;
|
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
resinfo = resp;
|
|
|
|
resp += large_len;
|
|
|
|
scanning -= large_len;
|
1999-09-01 20:53:43 +00:00
|
|
|
|
|
|
|
if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) {
|
2001-09-15 10:18:56 +00:00
|
|
|
if (dev) {
|
|
|
|
/*
|
|
|
|
* This is an optional device
|
|
|
|
* indentifier string. Skipt it
|
|
|
|
* for now.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* else mandately card identifier string */
|
1999-10-09 13:11:46 +00:00
|
|
|
if (large_len > sizeof(buf) - 1)
|
|
|
|
large_len = sizeof(buf) - 1;
|
|
|
|
bcopy(resinfo, buf, large_len);
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
/*
|
1999-10-09 13:11:46 +00:00
|
|
|
* Trim trailing spaces.
|
1999-09-01 20:53:43 +00:00
|
|
|
*/
|
1999-10-09 13:11:46 +00:00
|
|
|
while (buf[large_len-1] == ' ')
|
|
|
|
large_len--;
|
|
|
|
buf[large_len] = '\0';
|
|
|
|
desc = buf;
|
1999-09-01 20:53:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Small resource */
|
|
|
|
if (scanning < PNP_SRES_LEN(tag)) {
|
|
|
|
scanning = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
resinfo = resp;
|
|
|
|
resp += PNP_SRES_LEN(tag);
|
|
|
|
scanning -= PNP_SRES_LEN(tag);;
|
|
|
|
|
|
|
|
switch (PNP_SRES_NUM(tag)) {
|
|
|
|
case PNP_TAG_LOGICAL_DEVICE:
|
|
|
|
/*
|
|
|
|
* Parse the resources for the previous
|
|
|
|
* logical device (if any).
|
|
|
|
*/
|
|
|
|
if (startres) {
|
|
|
|
pnp_parse_resources(dev, startres,
|
2000-07-11 11:49:33 +00:00
|
|
|
resinfo - startres - 1,
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
ldn);
|
1999-10-09 13:11:46 +00:00
|
|
|
dev = 0;
|
|
|
|
startres = 0;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
/*
|
|
|
|
* A new logical device. Scan for end of
|
|
|
|
* resources.
|
|
|
|
*/
|
|
|
|
bcopy(resinfo, &logical_id, 4);
|
2000-07-11 11:49:33 +00:00
|
|
|
pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL);
|
1999-10-09 13:11:46 +00:00
|
|
|
dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
|
|
|
|
if (desc)
|
|
|
|
device_set_desc_copy(dev, desc);
|
2001-09-15 10:18:56 +00:00
|
|
|
else
|
|
|
|
device_set_desc_copy(dev,
|
|
|
|
pnp_eisaformat(logical_id));
|
1999-10-09 13:11:46 +00:00
|
|
|
isa_set_vendorid(dev, p->vendor_id);
|
|
|
|
isa_set_serial(dev, p->serial);
|
|
|
|
isa_set_logicalid(dev, logical_id);
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
isa_set_configattr(dev,
|
|
|
|
ISACFGATTR_CANDISABLE |
|
|
|
|
ISACFGATTR_DYNAMIC);
|
1999-10-09 13:11:46 +00:00
|
|
|
csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT);
|
|
|
|
if (!csnldn) {
|
|
|
|
device_printf(parent,
|
|
|
|
"out of memory\n");
|
1999-09-01 20:53:43 +00:00
|
|
|
scanning = 0;
|
1999-10-09 13:11:46 +00:00
|
|
|
break;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
csnldn->csn = csn;
|
|
|
|
csnldn->ldn = ldn;
|
|
|
|
ISA_SET_CONFIG_CALLBACK(parent, dev,
|
|
|
|
pnp_set_config, csnldn);
|
|
|
|
ldn++;
|
|
|
|
startres = resp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNP_TAG_END:
|
|
|
|
if (!startres) {
|
1999-09-01 20:53:43 +00:00
|
|
|
device_printf(parent,
|
1999-10-09 13:11:46 +00:00
|
|
|
"malformed resources\n");
|
1999-09-01 20:53:43 +00:00
|
|
|
scanning = 0;
|
|
|
|
break;
|
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
pnp_parse_resources(dev, startres,
|
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs.
- When constructing a resource configuration, respect the order
in which resource descriptors are read, in order to establish
the correct mapping between the descriptors and configuration
registers.
"Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5,
1994. "Clarifications to the Plug and Play ISA Specification,
Version 1.0a", Sec 6.2.1, Dec. 10, 1994.
- Do not ignore null (empty) descriptors; they are valid descriptors
acting as filler.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a",
Sec 6.2.1.
- Correctly set up logical device configuration registers for null
resources.
"Clarifications to the Plug and Play ISA Specification, Version 1.0a"
- Handle null resources properly in the resource allocator for the
ISA bus.
2001-09-05 03:54:33 +00:00
|
|
|
resinfo - startres - 1, ldn);
|
1999-10-09 13:11:46 +00:00
|
|
|
dev = 0;
|
|
|
|
startres = 0;
|
|
|
|
scanning = 0;
|
|
|
|
break;
|
1999-09-01 20:53:43 +00:00
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
default:
|
|
|
|
/* Skip this resource */
|
|
|
|
break;
|
1999-09-01 20:53:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
/*
|
|
|
|
* Read 'amount' bytes of resources from the card, allocating memory
|
|
|
|
* as needed. If a buffer is already available, it should be passed in
|
|
|
|
* '*resourcesp' and its length in '*spacep'. The number of resource
|
|
|
|
* bytes already in the buffer should be passed in '*lenp'. The memory
|
|
|
|
* allocated will be returned in '*resourcesp' with its size and the
|
|
|
|
* number of bytes of resources in '*spacep' and '*lenp' respectively.
|
2002-10-16 10:40:43 +00:00
|
|
|
*
|
|
|
|
* XXX: Multiple problems here, we forget to free() stuff in one
|
|
|
|
* XXX: error return, and in another case we free (*resourcesp) but
|
|
|
|
* XXX: don't tell the caller.
|
1999-10-09 13:11:46 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp)
|
|
|
|
{
|
|
|
|
u_char *resources = *resourcesp;
|
|
|
|
u_char *newres;
|
|
|
|
int space = *spacep;
|
|
|
|
int len = *lenp;
|
|
|
|
|
|
|
|
if (space == 0) {
|
|
|
|
space = 1024;
|
|
|
|
resources = malloc(space, M_TEMP, M_NOWAIT);
|
|
|
|
if (!resources)
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len + amount > space) {
|
|
|
|
int extra = 1024;
|
|
|
|
while (len + amount > space + extra)
|
|
|
|
extra += 1024;
|
|
|
|
newres = malloc(space + extra, M_TEMP, M_NOWAIT);
|
2003-05-31 20:21:53 +00:00
|
|
|
if (!newres) {
|
|
|
|
/* XXX: free resources */
|
1999-10-09 13:11:46 +00:00
|
|
|
return ENOMEM;
|
2003-05-31 20:21:53 +00:00
|
|
|
}
|
1999-10-09 13:11:46 +00:00
|
|
|
bcopy(resources, newres, len);
|
|
|
|
free(resources, M_TEMP);
|
|
|
|
resources = newres;
|
|
|
|
space += extra;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pnp_get_resource_info(resources + len, amount) != amount)
|
|
|
|
return EINVAL;
|
|
|
|
len += amount;
|
|
|
|
|
|
|
|
*resourcesp = resources;
|
|
|
|
*spacep = space;
|
|
|
|
*lenp = len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read all resources from the card, allocating memory as needed. If a
|
|
|
|
* buffer is already available, it should be passed in '*resourcesp'
|
|
|
|
* and its length in '*spacep'. The memory allocated will be returned
|
|
|
|
* in '*resourcesp' with its size and the number of bytes of resources
|
|
|
|
* in '*spacep' and '*lenp' respectively.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pnp_read_resources(u_char **resourcesp, int *spacep, int *lenp)
|
|
|
|
{
|
|
|
|
u_char *resources = *resourcesp;
|
|
|
|
int space = *spacep;
|
|
|
|
int len = 0;
|
|
|
|
int error, done;
|
|
|
|
u_char tag;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
done = 0;
|
|
|
|
while (!done) {
|
|
|
|
error = pnp_read_bytes(1, &resources, &space, &len);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
tag = resources[len-1];
|
|
|
|
if (PNP_RES_TYPE(tag) == 0) {
|
|
|
|
/*
|
|
|
|
* Small resource, read contents.
|
|
|
|
*/
|
|
|
|
error = pnp_read_bytes(PNP_SRES_LEN(tag),
|
|
|
|
&resources, &space, &len);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
if (PNP_SRES_NUM(tag) == PNP_TAG_END)
|
|
|
|
done = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Large resource, read length and contents.
|
|
|
|
*/
|
|
|
|
error = pnp_read_bytes(2, &resources, &space, &len);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = pnp_read_bytes(resources[len-2]
|
|
|
|
+ (resources[len-1] << 8),
|
|
|
|
&resources, &space, &len);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
*resourcesp = resources;
|
|
|
|
*spacep = space;
|
|
|
|
*lenp = len;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
/*
|
|
|
|
* Run the isolation protocol. Use pnp_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
|
|
|
|
* pnp_rd_port as the READ_DATA port.
|
|
|
|
*
|
|
|
|
* In the first pass, a csn is assigned to each board and pnp_id's
|
|
|
|
* are saved to an array, pnp_devices. In the second pass, each
|
|
|
|
* card is woken up and the device configuration is called.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pnp_isolation_protocol(device_t parent)
|
|
|
|
{
|
|
|
|
int csn;
|
|
|
|
pnp_id id;
|
1999-10-09 13:11:46 +00:00
|
|
|
int found = 0, len;
|
|
|
|
u_char *resources = 0;
|
|
|
|
int space = 0;
|
|
|
|
int error;
|
2002-09-29 13:31:26 +00:00
|
|
|
#ifdef PC98
|
|
|
|
int n, necpnp;
|
|
|
|
u_char buffer[10];
|
|
|
|
#endif
|
1999-09-01 20:53:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Put all cards into the Sleep state so that we can clear
|
|
|
|
* their CSNs.
|
|
|
|
*/
|
|
|
|
pnp_send_initiation_key();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the CSN for all cards.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move all cards to the Isolation state.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_WAKE, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell them where the read point is going to be this time.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_SET_RD_DATA, pnp_rd_port);
|
|
|
|
|
|
|
|
for (csn = 1; csn < PNP_MAX_CARDS; csn++) {
|
|
|
|
/*
|
|
|
|
* Start the serial isolation protocol.
|
|
|
|
*/
|
|
|
|
outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
|
|
|
|
DELAY(1000); /* Delay 1 msec */
|
|
|
|
|
|
|
|
if (pnp_get_serial(&id)) {
|
|
|
|
/*
|
|
|
|
* We have read the id from a card
|
|
|
|
* successfully. The card which won the
|
|
|
|
* isolation protocol will be in Isolation
|
1999-10-09 13:11:46 +00:00
|
|
|
* mode and all others will be in Sleep.
|
1999-09-01 20:53:43 +00:00
|
|
|
* Program the CSN of the isolated card
|
|
|
|
* (taking it to Config state) and read its
|
|
|
|
* resources, creating devices as we find
|
|
|
|
* logical devices on the card.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_SET_CSN, csn);
|
2002-09-29 13:31:26 +00:00
|
|
|
#ifdef PC98
|
|
|
|
if (bootverbose)
|
|
|
|
printf("PnP Vendor ID = %x\n", id.vendor_id);
|
|
|
|
/* Check for NEC PnP (9 bytes serial). */
|
|
|
|
for (n = necpnp = 0; necids[n].vendor_id; n++) {
|
|
|
|
if (id.vendor_id == necids[n].vendor_id) {
|
|
|
|
necpnp = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (necpnp) {
|
|
|
|
if (bootverbose)
|
|
|
|
printf("It seems to NEC-PnP card (%s).\n",
|
|
|
|
pnp_eisaformat(id.vendor_id));
|
|
|
|
/* Read dummy 9 bytes serial area. */
|
|
|
|
pnp_get_resource_info(buffer, 9);
|
|
|
|
} else {
|
|
|
|
if (bootverbose)
|
|
|
|
printf("It seems to Normal-ISA-PnP card (%s).\n",
|
|
|
|
pnp_eisaformat(id.vendor_id));
|
|
|
|
}
|
|
|
|
if (bootverbose)
|
|
|
|
printf("Reading PnP configuration for %s.\n",
|
|
|
|
pnp_eisaformat(id.vendor_id));
|
|
|
|
#endif
|
1999-10-09 13:11:46 +00:00
|
|
|
error = pnp_read_resources(&resources,
|
|
|
|
&space,
|
|
|
|
&len);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
pnp_create_devices(parent, &id, csn,
|
|
|
|
resources, len);
|
1999-09-01 20:53:43 +00:00
|
|
|
found++;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put this card back to the Sleep state and
|
|
|
|
* simultaneously move all cards which don't have a
|
|
|
|
* CSN yet to Isolation state.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_WAKE, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unless we have chosen the wrong read port, all cards will
|
|
|
|
* be in Sleep state. Put them back into WaitForKey for
|
|
|
|
* now. Their resources will be programmed later.
|
|
|
|
*/
|
|
|
|
pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
|
|
|
|
|
1999-10-09 13:11:46 +00:00
|
|
|
/*
|
|
|
|
* Cleanup.
|
|
|
|
*/
|
|
|
|
if (resources)
|
|
|
|
free(resources, M_TEMP);
|
|
|
|
|
1999-09-01 20:53:43 +00:00
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pnp_identify()
|
|
|
|
*
|
|
|
|
* autoconfiguration of pnp devices. This routine just runs the
|
|
|
|
* isolation protocol over several ports, until one is successful.
|
|
|
|
*
|
|
|
|
* may be called more than once ?
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
pnp_identify(driver_t *driver, device_t parent)
|
|
|
|
{
|
|
|
|
int num_pnp_devs;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (pnp_ldn_overrides[0].csn == 0) {
|
|
|
|
if (bootverbose)
|
|
|
|
printf("Initializing PnP override table\n");
|
|
|
|
bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides));
|
|
|
|
pnp_ldn_overrides[0].csn = 255 ;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Try various READ_DATA ports from 0x203-0x3ff */
|
|
|
|
for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
|
|
|
|
if (bootverbose)
|
|
|
|
printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3);
|
|
|
|
|
|
|
|
num_pnp_devs = pnp_isolation_protocol(parent);
|
|
|
|
if (num_pnp_devs)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static device_method_t pnp_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_identify, pnp_identify),
|
|
|
|
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t pnp_driver = {
|
|
|
|
"pnp",
|
|
|
|
pnp_methods,
|
|
|
|
1, /* no softc */
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t pnp_devclass;
|
|
|
|
|
|
|
|
DRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0);
|