/****************************************************************************
*
* HWI_PCMC.C : Part of the FASTMAC TOOL-KIT (FTK)
*
* THE HARDWARE INTERFACE MODULE FOR PCMCIA CARDS
*
* Copyright (c) Madge Networks Ltd. 1990-1994
*
* COMPANY CONFIDENTIAL
*
****************************************************************************
*
* The purpose of the Hardware Interface (HWI) is to supply an adapter card
* independent interface to any driver. It performs nearly all of the
* functions that involve affecting SIF registers on the adapter cards.
* This includes downloading code to, initializing, and removing adapters.
*
* The HWI_PCMC.C module contains the routines specific to PCMCIA cards
* which are necessary to install an adapter, to initialize an adapter, to
* remove an adapter and to handle interrupts on an adapter.
*
****************************************************************************/
/*---------------------------------------------------------------------------
|
| DEFINITIONS
|
---------------------------------------------------------------------------*/
#include "ftk_defs.h"
/*---------------------------------------------------------------------------
|
| MODULE ENTRY POINTS
|
---------------------------------------------------------------------------*/
#include "ftk_intr.h" /* routines internal to FTK */
#include "ftk_extr.h" /* routines provided or used by external FTK user */
#ifndef FTK_NO_PCMCIA
#ifndef FTK_NO_PROBE
/*---------------------------------------------------------------------------
|
| Card Services related defines and globals. Used only in this module.
|
---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
|
| #defines
|
---------------------------------------------------------------------------*/
#define DETERMINED_BY_CARD_SERVICES 0x00FF
#define MAX_PCMCIA_SOCKETS 0x08
#define PCMCIA_CS_REGISTRATION_TIMEOUT 0xF0000 /* Not real time. Just a */
/* for loop counting so */
/* 0xF0000 times. */
/*---------------------------------------------------------------------------
|
| These are hardware related defines. These are not put in ftk_pcmc.h
| because: 1. they are only used here and not by ftk user. 2. they are
| defined using #defines in sys_cs.h which is only included internally.
|
---------------------------------------------------------------------------*/
/*
* Interrupt channels supported in form of bit mask.
*/
#define PCMCIA_AVAILABLE_IRQ_MASK \
\
(IRQ_2|IRQ_3|IRQ_5|IRQ_6|IRQ_7|IRQ_8|IRQ_10|IRQ_11|IRQ_12|IRQ_13|IRQ_14|IRQ_15)
/*
* This is the value of IRQInfo1 used in RequestConfiguration. Note that
* only level triggered interupt is supported.
*/
/*
* User has specified an interrupt channel.
*/
#define PCMCIA_IRQINFO1_SPECIFIED IRQ_INFO1_LEVEL
/*
* User has not specified an interrupt channel, will be allocated by PCMCIA
* card services.
*/
#define PCMCIA_IRQINFO1_NOT_SPECIFIED \
\
(IRQ_INFO1_LEVEL | IRQ_INFO1_INFO2_ENABLE)
/*---------------------------------------------------------------------------
|
| PCMCIA client information
|
---------------------------------------------------------------------------*/
#define PCMCIA_VENDOR_NAME "MADGE"
#define PCMCIA_VENDOR_NAME_LEN 6
/* 1234567890123456789012345678901 */
#define PCMCIA_CLIENT_NAME "SMART 16/4 PCMCIA RINGNODE HWI"
#define PCMCIA_CLIENT_NAME_LEN 31
#define PCMCIA_VENDOR_REVISION 0x0001
#define PCMCIA_VENDOR_REVISION_DATE 0x1C2D /* 13/01/94 in dos format */
struct STRUCT_MADGE_CLIENT_INFODD
{
CS_CLIENT_INFO info;
BYTE NameString[PCMCIA_CLIENT_NAME_LEN];
BYTE VendorString[PCMCIA_VENDOR_NAME_LEN];
};
typedef struct STRUCT_MADGE_CLIENT_INFODD MADGE_CLIENT_INFO;
#define DEFAULT_CLIENT_INFO \
{ \
{ \
0x0000, \
sizeof(MADGE_CLIENT_INFO), \
RC_ATTR_IO_CLIENT_DEVICE_DRIVER | RC_ATTR_IO_INSERTION_SHARABLE, \
PCMCIA_VENDOR_REVISION, \
CARD_SERVICES_VERSION, \
PCMCIA_VENDOR_REVISION_DATE, \
20, \
PCMCIA_CLIENT_NAME_LEN, \
20+PCMCIA_CLIENT_NAME_LEN, \
PCMCIA_VENDOR_NAME_LEN, \
}, \
PCMCIA_CLIENT_NAME, \
PCMCIA_VENDOR_NAME, \
}
/*---------------------------------------------------------------------------
|
| PCMCIA sockets record structures
|
---------------------------------------------------------------------------*/
enum ENUM_SOCKET_STATUS
{
SOCKET_NOT_INITIALIZED = 0,
SOCKET_READY,
SOCKET_IN_USE,
};
typedef enum ENUM_SOCKET_STATUS SOCKET_STATUS;
struct STRUCT_PCMCIA_SOCKET_RECORD
{
WORD ClientHandle;
WORD io_location;
WORD irq_number;
SOCKET_STATUS status;
ADAPTER_HANDLE adapter_handle;
};
typedef struct STRUCT_PCMCIA_SOCKET_RECORD PCMCIA_SOCKET_RECORD;
/*---------------------------------------------------------------------------
|
| Things use to set up argument buffer
|
---------------------------------------------------------------------------*/
/*
* Default arg buffer length.
*/
#define MAX_PCMCIA_ARG_BUFFER_LEN 100
/*
* Arg buffer structure.
*/
/*
* Madge Card Services expects the string "Madge" prepended to the argument
* buffer.
*/
struct STRUCT_PCMCIA_ARG_BUFFER
{
BYTE Madge[5];
BYTE Buf[MAX_PCMCIA_ARG_BUFFER_LEN];
};
typedef struct STRUCT_PCMCIA_ARG_BUFFER PCMCIA_ARG_BUFFER;
/*
* A macro which creates a new argument buffer and initializes it.
*/
#define NEW_PCMCIA_ARG_BUFFER(Fp) \
\
PCMCIA_ARG_BUFFER _xXx_arg_buf_##Fp = {{'M','a','d','g','e'}, {0x00}}; \
BYTE FAR * ##Fp = (BYTE FAR *)(_xXx_arg_buf_##Fp.Buf)
/*---------------------------------------------------------------------------
|
| Global variables Used by Card Services related routines
|
---------------------------------------------------------------------------*/
/*
* PCMCIA Socket Record. One for each socket. Index by socket no. i.e. 0 to
* MAX_PCMCIA_SOCKETS-1
*/
PCMCIA_SOCKET_RECORD pcmcia_socket_table[MAX_PCMCIA_SOCKETS] =
{
{0x0000, 0x0000, 0x0000, SOCKET_NOT_INITIALIZED, 0x0000},
};
WORD CardServicesVersion; /* Version of Card Services found */
/*
* A flag set by callback to signal of registration completion
*/
WBOOLEAN RegisterClientCompleted = FALSE;
/*
* A signature string found on Madge 16 4 PCMCIA Ringnode. Use in adapter
* groping.
*/
BYTE MADGE_TPLLV1_INFO[] = MADGE_TPLLV1_INFO_STRING;
/*
* A bit mask of interrupt channel current used by active ringnode.
*/
WORD UsedIrqChannelsMask = 0x0000;
/*
* The default client information. Reply with this for CLIENT_INFO callback.
*/
MADGE_CLIENT_INFO default_client_info = DEFAULT_CLIENT_INFO;
#endif
/*---------------------------------------------------------------------------
|
| LOCAL PROCEDURES
|
---------------------------------------------------------------------------*/
local WORD
hwi_pcmcia_read_eeprom_word(
ADAPTER * adapter,
WORD word_address);
local WORD
pcmcia_c46_read_data(
ADAPTER * adapter);
local void
pcmcia_c46_write_bit(
ADAPTER * adapter,
WORD mask,
WBOOLEAN set_bit);
local void
pcmcia_c46_twitch_clock(
ADAPTER * adapter);
#ifndef FTK_NO_PROBE
local WBOOLEAN
hwi_pcmcia_card_services_setup(
PROBE * resource
);
local WORD
hwi_pcmcia_cs_release_config(
WORD ClientHandle,
WORD Socket
);
local WORD
hwi_pcmcia_cs_release_io(
WORD ClientHandle,
WORD Socket,
WORD IoLocation
);
local WORD
hwi_pcmcia_cs_release_irq(
WORD ClientHandle,
WORD Socket,
WORD IRQChannel
);
local WORD
hwi_pcmcia_cs_deregister_client(
WORD ClientHandle
);
local WBOOLEAN
hwi_pcmcia_tear_down_cs(
PROBE resource
);
#endif
#ifndef FTK_NO_PROBE
/****************************************************************************
*
* hwi_pcmcia_probe_card
* =====================
*
*
* PARAMETERS (passed by hwi_probe_adapter) :
* ==========================================
*
* PROBE * resources
*
* resources is an array structures used to identify and record specific
* information about adapters found.
*
* UINT length
*
* length is the number of structures pointed to by reources.
*
* WORD * valid_locations
*
* valid_locations is an array of IO locations to examine for the presence
* of an adapter. For PCMCIA adapters with should be a subset of
* {0x0a20, 0x1a20, 0x2a20, 0x3a20}.
*
* UINT number_locations
*
* This is the number of IO locations in the above list.
*
* BODY :
* ======
*
* The hwi_pcmcia_probe_card routine is called by hwi_probe_adapter. It
* reads the id registers to find the type of card and also reads the IRQ.
*
*
* RETURNS :
* =========
*
* The routine returns the number of adapters found, or PROBE_FAILURE if
* there's a problem.
*
****************************************************************************/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(hwi_pcmcia_probe_card)
#endif
export UINT
hwi_pcmcia_probe_card(
PROBE * resources,
UINT length,
WORD * valid_locations,
UINT number_locations
)
{
UINT i;
UINT j;
/*
* Check the bounds to make sure they're sensible
*/
if(length <= 0 || number_locations <= 0)
{
return PROBE_FAILURE;
}
/*
* j is the number of adapters found.
*/
j = 0;
for(i = 0; i < number_locations; i++)
{
/*
* If we've run out of PROBE structures then return.
*/
if(j >= length)
{
return(j);
}
#ifndef FTK_NO_IO_ENABLE
macro_probe_enable_io(valid_locations[i], PCMCIA_IO_RANGE);
#endif
resources[j].io_location = valid_locations[i];
if ( hwi_pcmcia_card_services_setup(&resources[j]) )
{
resources[j].adapter_card_type =
ADAPTER_CARD_TYPE_16_4_PCMCIA;
resources[j].adapter_ram_size =
PCMCIA_RAM_SIZE;
resources[j].adapter_card_bus_type =
ADAPTER_CARD_PCMCIA_BUS_TYPE;
resources[j].dma_channel =
0;
resources[j].transfer_mode =
PIO_DATA_TRANSFER_MODE;
/*
* Increment j to point at the next PROBE structure.
*/
j++;
/*
* HACK!! Card Services doesn't seem to be re-entrant so quit
* straight away.
*/
return j;
}
#ifndef FTK_NO_IO_ENABLE
macro_probe_disable_io(resources->io_location, PCMCIA_IO_RANGE);
#endif
}
return(j);
}
/****************************************************************************
*
* hwi_pcmcia_deprobe_card
* =======================
*
*
* PARAMETERS (passed by hwi_probe_adapter) :
* ==========================================
*
* PROBE resource
*
* This structure is used to identify and record specific information about
* the adapter.
*
* BODY :
* ======
*
* The hwi_smart16_probe_card routine is called by hwi_probe_adapter. It
* probes the adapter card for information such as DMA channel, IRQ number
* etc. This information can then be supplied by the user when starting the
* adapter.
*
*
* RETURNS :
* =========
*
* The routine returns the number of adapters found, or zero if there's a
* problem
*
****************************************************************************/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_deprobe_card)
#endif
export WBOOLEAN
hwi_pcmcia_deprobe_card(
PROBE resource
)
{
WBOOLEAN success;
#ifndef FTK_NO_IO_ENABLE
macro_probe_enable_io(resource.io_location, PCMCIA_IO_RANGE);
#endif
/*
* Release resources requested from card services and deregister.
*/
success = hwi_pcmcia_tear_down_cs(resource);
#ifndef FTK_NO_IO_ENABLE
macro_probe_disable_io(resource.io_location, PCMCIA_IO_RANGE);
#endif
return(success);
}
#endif
/****************************************************************************
*
* hwi_pcmcia_install_card
* =======================
*
*
* PARAMETERS (passed by hwi_install_adapter) :
* ============================================
*
* ADAPTER * adapter
*
* This structure is used to identify and record specific information about
* the required adapter.
*
* DOWNLOAD_IMAGE * download_image
*
* This is the code to be downloaded to the adapter. The image must be of
* the correct type i.e. must be downloadable into the adapter. If the
* pointer is 0 downloading is not done.
*
*
* BODY :
* ======
*
* The hwi_pcmcia_install_card routine is called by hwi_install_adapter.
* It sets up the adapter card and downloads the required code to it.
*
* Firstly, it checks if the I O location and interrupt channel are valid
* Then it registers with PCMCIA card services, and checks if the required
* Madge PCMCIA ringnode exists. If so it requests interrrupt and I
* resources from PCMCIA card services, sets up and checks numerous
* on-board registers for correct operation. Burnt in address and default
* ring speed are read from EEPROM.
*
* Then, it halts the EAGLE, downloads the code, restarts the EAGLE and
* waits up to 3 seconds for a valid bring-up code. If interrupts are
* required, these are enabled by operating system specific calls.
*
*
* RETURNS :
* =========
*
* The routine returns TRUE if it succeeds. If this routine fails (returns
* FALSE) then a subsequent call to driver_explain_error, with the adapter
* handle corresponding to the adapter parameter used here, will give an
* explanation.
*
****************************************************************************/
void hlpr_unmap_PCMCIA_irq(WORD irq);
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(hwi_pcmcia_install_card)
#endif
export WBOOLEAN
hwi_pcmcia_install_card(
ADAPTER * adapter,
DOWNLOAD_IMAGE * download_image
)
{
WORD control_1 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_1;
WORD control_2 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_2;
WORD eeprom_word;
WORD ring_speed;
WORD sif_base;
BYTE tmp_byte;
#ifdef PCMCIA_POINT_ENABLE
/*
* Make sure we don't lose any interrupts.
*/
adapter->drop_int = FALSE;
/*
* Enable the card.
*/
if(!sys_pcmcia_point_enable(adapter))
{
/*
* Error record already filled in.
*/
return(FALSE);
}
#endif
/*
* We don't do any validation on the user supplied adapter details
* since the user has to obtain the values from card services in the
* first place! (Or did they?)
*/
/*
* Save IO location of the SIF registers.
*/
sif_base = adapter->io_location + PCMCIA_FIRST_SIF_REGISTER;
adapter->sif_dat = sif_base + EAGLE_SIFDAT;
adapter->sif_datinc = sif_base + EAGLE_SIFDAT_INC;
adapter->sif_adr = sif_base + EAGLE_SIFADR;
adapter->sif_int = sif_base + EAGLE_SIFINT;
adapter->sif_acl = sif_base + PCMCIA_EAGLE_SIFACL;
adapter->sif_adr2 = sif_base + PCMCIA_EAGLE_SIFADR_2;
adapter->sif_adx = sif_base + PCMCIA_EAGLE_SIFADX;
adapter->sif_dmalen = sif_base + PCMCIA_EAGLE_DMALEN;
adapter->io_range = PCMCIA_IO_RANGE;
#ifndef FTK_NO_IO_ENABLE
macro_enable_io(adapter);
#endif
/*
* Can be assumed for a PCMCIA adapter.
*/
adapter->adapter_card_type = ADAPTER_CARD_TYPE_16_4_PCMCIA;
#if 0
adapter->adapter_card_revision = 0x0000;
adapter->adapter_ram_size = PCMCIA_RAM_SIZE;
#endif
/*
* Interrupts are always level triggered.
*/
adapter->edge_triggered_ints = FALSE;
/*
* To get the software handeshake to work properly on PIO
* we need to set the MC_AND_ISACP_USE_PIO bit in the
* mc32_config byte.
*/
adapter->mc32_config = MC_AND_ISACP_USE_PIO;
/*
* Read node address from serial EEPROM.
*/
eeprom_word = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_NODEADDR1);
adapter->permanent_address.byte[0] = (BYTE)(eeprom_word & 0x00FF);
adapter->permanent_address.byte[1] = (BYTE)(eeprom_word >> 8);
eeprom_word = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_NODEADDR2);
adapter->permanent_address.byte[2] = (BYTE)(eeprom_word & 0x00FF);
adapter->permanent_address.byte[3] = (BYTE)(eeprom_word >> 8);
eeprom_word = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_NODEADDR3);
adapter->permanent_address.byte[4] = (BYTE)(eeprom_word & 0x00FF);
adapter->permanent_address.byte[5] = (BYTE)(eeprom_word >> 8);
/*
* Read ring speed from serial EEPROM.
*/
ring_speed = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_RINGSPEED);
/*
* Read RAM size from serial EEPROM. Use 512k default if nothing
* specified.
*/
adapter->adapter_ram_size = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_RAMSIZE
) * 128;
if (adapter->adapter_ram_size == 0)
{
adapter->adapter_ram_size = PCMCIA_RAM_SIZE;
}
/*
* Read hardware revision from serial EEPROM.
*/
adapter->adapter_card_revision = hwi_pcmcia_read_eeprom_word(
adapter,
EEPROM_ADDR_REVISION);
adapter->use_32bit_pio = FALSE;
#ifdef FTK_PCMCIA_32BIT_PIO
if (adapter->adapter_card_revision != 0)
{
adapter->use_32bit_pio = TRUE;
}
#endif
/*
* Set all the EEPROM related bits in control register 2 to zero.
*/
tmp_byte = 0x00;
tmp_byte &= ~( PCMCIA_CTRL2_E2DO | PCMCIA_CTRL2_E2DI |
PCMCIA_CTRL2_E2CS | PCMCIA_CTRL2_E2SK |
PCMCIA_CTRL2_FLSHWREN );
/*
* Set SIF clock frequency to 16MHz.
*/
tmp_byte |= PCMCIA_CTRL2_SBCKSEL_16;
/*
* Ring Speed is read from EEPROM, now program it to the front
* end chip.
*/
if (adapter->set_ring_speed == 16)
{
tmp_byte |= ( PCMCIA_CTRL2_4N16 & PCMCIA_CTRL2_4N16_16 );
}
else if (adapter->set_ring_speed == 4)
{
tmp_byte |= ( PCMCIA_CTRL2_4N16 & PCMCIA_CTRL2_4N16_4 );
}
else if ( ring_speed == EEPROM_RINGSPEED_4 )
{
tmp_byte |= ( PCMCIA_CTRL2_4N16 & PCMCIA_CTRL2_4N16_4 );
}
else
{
tmp_byte |= ( PCMCIA_CTRL2_4N16 & PCMCIA_CTRL2_4N16_16 );
}
/*
* Write all these setting to control register 2 all in one go.
*/
sys_outsb(adapter->adapter_handle, control_2, tmp_byte);
/*
* Bring EAGLE out of reset.
*/
macro_setb_bit(
adapter->adapter_handle,
control_1,
PCMCIA_CTRL1_SRESET);
/*
* Halt EAGLE, no need to page in extended SIF registers for pcmcia.
*/
hwi_halt_eagle(adapter);
/*
* Download code to adapter. View download image as a sequence of
* download records.
* Pass address of routine to set up DIO addresses on pcmcia cards. If
* routine fails return failure (error record already filled in).
*/
if (!hwi_download_code(
adapter,
(DOWNLOAD_RECORD *) download_image,
hwi_pcmcia_set_dio_address))
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
return FALSE;
}
/*
* Start EAGLE, no need page in extended SIF registers for pcmcia cards.
*/
hwi_start_eagle(adapter);
/*
* Wait for a valid bring up code, may wait 3 seconds. If routine fails
* return failure (error record already filled in).
*/
if (!hwi_get_bring_up_code(adapter))
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
return FALSE;
}
/*
* Set DIO address to point to EAGLE DATA page 0x10000L.
*/
hwi_pcmcia_set_dio_address(adapter, DIO_LOCATION_EAGLE_DATA_PAGE);
/*
* Set maximum frame size from the ring speed.
*/
adapter->max_frame_size = hwi_get_max_frame_size(adapter);
/*
* Get the ring speed.
*/
adapter->ring_speed = hwi_get_ring_speed(adapter);
/*
* If not in polling mode then set up interrupts interrupts_on field
* is used when disabling interrupts for adapter.
*/
if (adapter->interrupt_number != POLLING_INTERRUPTS_MODE)
{
adapter->interrupts_on = sys_enable_irq_channel(
adapter->adapter_handle,
adapter->interrupt_number);
if (!adapter->interrupts_on)
{
adapter->error_record.type = ERROR_TYPE_HWI;
adapter->error_record.value = HWI_E_0B_FAIL_IRQ_ENABLE;
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
return FALSE;
}
}
else
{
adapter->interrupts_on = TRUE;
}
/*
* Enable interrupts at adapter GCR1 SINTREN.
*/
macro_setb_bit(
adapter->adapter_handle,
control_1,
PCMCIA_CTRL1_SINTREN);
/*
* PCMCIA adapters do not have DMA channel to setup.
*/
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
return TRUE;
}
/****************************************************************************
*
* hwi_pcmcia_interrupt_handler
* ============================
*
*
* PARAMETERS (passed by hwi_interrupt_entry) :
* ============================================
*
* ADAPTER_HANDLE adapter_handle
*
* The adapter handle for the adapter so it can later be passed to the user
* supplied user_receive_frame or user_completed_srb routine.
*
* ADAPTER * adapter
*
* This structure is used to identify and record specific information about
* the required adapter.
*
*
* BODY :
* ======
*
* The hwi_pcmcia_interrupt_handler routine is called, when an interrupt
* occurs, by hwi_interrupt_entry. It checks to see if a particular card
* has interrupted. The interrupt could be generated by the SIF or a PIO
* data transfer. Note it could in fact be the case that no interrupt has
* occured on the particular adapter being checked.
*
* On SIF interrupts, the interrupt is acknowledged and cleared. The value
* in the SIF interrupt register is recorded in order to pass it to the
* driver_interrupt_entry routine (along with the adapter details).
*
* On PIO interrupts, the length, direction and physical address of the
* transfer is determined. A system provided routine is called to do the
* data transfer itself. Note the EAGLE thinks it is doing a DMA transfer.
*
*
* RETURNS :
* =========
*
* The routine always successfully completes.
*
****************************************************************************/
#ifdef FTK_IRQ_FUNCTION
#pragma FTK_IRQ_FUNCTION(hwi_pcmcia_interrupt_handler)
#endif
#ifndef WIN_BOOK_FIX
/*---------------------------------------------------------------------------
Ý
Ý This is the ordinary, none WinBook, interrupt handler.
Ý
Ý--------------------------------------------------------------------------*/
export void
hwi_pcmcia_interrupt_handler(
ADAPTER * adapter
)
{
ADAPTER_HANDLE adapter_handle = adapter->adapter_handle;
WORD control_1 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_1;
WORD control_2 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_2;
WORD pio_location = adapter->io_location +
PCMCIA_PIO_IO_LOC;
WORD sifint_register = adapter->sif_int;
WORD sifadr = adapter->sif_adr;
WORD sifdat = adapter->sif_dat;
WORD sifacl = adapter->sif_acl;
WORD sifint_value;
WORD sifint_tmp;
BYTE FAR * pio_address;
WORD pio_len_bytes;
WORD pio_from_adapter;
WORD saved_sifadr;
WORD dummy;
DWORD dma_high_word;
WORD dma_low_word;
WORD temp_word;
#ifndef FTK_NO_IO_ENABLE
macro_enable_io( adapter);
#endif
/*
* Check for SIF interrupt or PIO interrupt.
*/
if ((sys_insw(adapter_handle, sifint_register) &
FASTMAC_SIFINT_IRQ_DRIVER) != 0)
{
/*
* A SIF interrupt has occurred. Thsi could be an SRB free,
* an adapter check or a received frame interrupt.
*/
/*
* Clear SIF interrupt enable bit in control register.
*/
macro_clearb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SINTREN);
/*
* Clear EAGLE_SIFINT_HOST_IRQ to acknowledge interrupt at SIF.
*/
/*
* WARNING: Do NOT reorder the clearing of the SIFINT register with
* the reading of it. If SIFINT is cleared after reading it, any
* interrupts raised after reading it will be lost. Admittedly
* this is a small time frame, but it is important.
*/
sys_outsw(adapter_handle, sifint_register, 0);
/*
* Record the EAGLE SIF interrupt register value.
*/
/*
* WARNING: Continue to read the SIFINT register until it is stable
* because of a potential problem involving the host reading the
* register after the adapter has written the low byte of it, but
* before it has written the high byte. Failure to wait for the
* SIFINT register to settle can cause spurious interrupts.
*/
sifint_value = sys_insw(adapter_handle, sifint_register);
do
{
sifint_tmp = sifint_value;
sifint_value = sys_insw(adapter_handle, sifint_register);
}
while (sifint_tmp != sifint_value);
/*
* Acknowledge clear interrupt at interrupt controller.
*/
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number
);
#endif
/*
* Set interrupt enable bit to regenerate any lost interrupts.
*/
macro_setb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SINTREN);
/*
* It is possible that the PCMCIA adapter may have been removed. In
* which case we would expect to get 0xffff from an IO location
* occupied by the adapter. We will check the value of SIFADR since
* this should never be 0xffff.
*/
if (sys_insw(adapter_handle, sifadr) == 0xffff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io( adapter);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
/*
* Call driver with details of SIF interrupt.
*/
driver_interrupt_entry(adapter_handle, adapter, sifint_value);
}
else if ((sys_insb(adapter_handle, control_1) & PCMCIA_CTRL1_SHRQ) != 0)
{
/*
* A PIO interrupt has occurred. Transfer data to/from adapter.
*/
saved_sifadr = sys_insw(adapter_handle, sifadr);
/*
* Read the physical address for the PIO through DIO space from the
* SIF registers. Because the SIF thinks that it is doing real DMA,
* the SDMAADR SDMAADX registers cannot be paged in, so they must
* be read from their memory mapped locations in Eagle memory.
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_EXT_DMA_ADDR);
dma_high_word = (DWORD)sys_insw(adapter_handle, sifdat);
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_ADDR);
dma_low_word = sys_insw(adapter_handle, sifdat);
pio_address = (BYTE FAR *) ((dma_high_word << 16) |
((DWORD) dma_low_word));
/*
* Read the PIO length from SIF register.
*/
pio_len_bytes = sys_insw(adapter_handle, adapter->sif_dmalen);
/*
* Determine what direction the data transfer is to take place in.
*/
pio_from_adapter = sys_insw(
adapter_handle,
sifacl) & EAGLE_SIFACL_SWDDIR;
/*
* It is possible that the PCMCIA adapter may have been removed. In
* which case we would expect to get 0xffff from an IO location
* occupied by the adapter. We will check the value of pio_len_bytes
* since this should never be 0xffff. We do this test here as we have
* read what we think are the DMA location and length. The following
* code may DMA rubbish if the adapter goes away after this test
* but at least we know it will happen to/from a valid host buffer.
*/
if (pio_len_bytes == 0xffff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number
);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
/*
* We need to use software handshaking across the PIO.
* Start by writing zero to a magic location on the adapter.
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
sys_outsw(adapter_handle, sifdat, 0);
/*
* Start what the SIF thinks is a DMA but is PIO instead. This
* involves clearing the SINTREN bit and setting SHLDA bit on
* control register 1. Note that we have to keep SRESET bit set
* otherwise we will reset the EAGLE.
*/
sys_outsb(
adapter_handle,
control_1,
PCMCIA_CTRL1_SHLDA | PCMCIA_CTRL1_SRESET);
/*
* Do the actual data transfer.
*/
/*
* Note that Fastmac only copies whole WORDs to DWORD boundaries
* FastmacPlus, however, can transfer any length to any address.
*/
if (pio_from_adapter)
{
/*
*
*
* Transfer into host memory from adapter.
*
*
*/
/*
* Deal with an odd leading byte.
*/
if (((card_t) pio_address & 0x01) != 0)
{
/*
* Read a WORD, the top byte is data.
*/
temp_word = sys_insw(adapter_handle, pio_location);
pio_len_bytes--;
*(pio_address++) = (BYTE) (temp_word >> 8);
}
/*
* We might be able to do 32 bit PIO.
*/
if (adapter->use_32bit_pio)
{
/*
* Deal with an odd leading word.
*/
if (((card_t) pio_address & 0x02) != 0 && pio_len_bytes > 0)
{
/*
* There could be one or two bytes of data.
*/
if (pio_len_bytes == 1)
{
pio_len_bytes--;
*(pio_address) =
sys_insb(adapter_handle, pio_location);
}
else
{
pio_len_bytes -= 2;
*((WORD *) pio_address) =
sys_insw(adapter_handle, pio_location);
pio_address += 2;
}
}
/*
* Deal with the bulk of the dwords.
*/
sys_rep_insd(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 2)
);
pio_address += (pio_len_bytes & 0xfffc);
/*
* Deal with a trailing word.
*/
if ((pio_len_bytes & 0x02) != 0)
{
/*
* Read a word.
*/
*((WORD *) pio_address) =
sys_insw(adapter_handle, pio_location);
pio_address += 2;
}
}
/*
* Otherwise use 16 bit PIO.
*/
else
{
/*
* Transfer the bulk of the data.
*/
sys_rep_insw(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 1)
);
pio_address += (pio_len_bytes & 0xfffe);
}
/*
* Finally transfer any trailing byte.
*/
if ((pio_len_bytes & 0x01) != 0)
{
*(pio_address) = sys_insb(adapter_handle, pio_location);
}
}
else
{
/*
*
*
* Transfer into adapter memory from the host.
*
*
*/
/*
* Deal with a leading odd byte.
*/
if (((card_t) pio_address & 0x01) != 0)
{
/*
* Write a WORD with data in top byte.
*/
temp_word = ((WORD) *(pio_address++)) << 8;
pio_len_bytes--;
sys_outsw(adapter_handle, pio_location, temp_word);
}
/*
* We might be able to do 32 bit PIO.
*/
if (adapter->use_32bit_pio)
{
/*
* Deal with an odd leading word.
*/
if (((card_t) pio_address & 0x02) != 0 && pio_len_bytes > 0)
{
/*
* There could be one or two bytes of data. If there
* is only one byte it goes in the high word.
*/
if (pio_len_bytes == 1)
{
pio_len_bytes--;
sys_outsb(
adapter_handle,
pio_location,
*(pio_address)
);
}
else
{
pio_len_bytes -= 2;
sys_outsw(
adapter_handle,
pio_location,
*((WORD *) pio_address)
);
pio_address += 2;
}
}
/*
* Deal with the bulk of the dwords.
*/
sys_rep_outsd(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 2)
);
pio_address += (pio_len_bytes & 0xfffc);
/*
* Deal with a trailing word.
*/
if ((pio_len_bytes & 0x02) != 0)
{
/*
* Write a word.
*/
sys_outsw(
adapter_handle,
pio_location,
*((WORD *) pio_address)
);
pio_address += 2;
}
}
/*
* Otherwise use 16 bit PIO.
*/
else
{
sys_rep_outsw(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 1)
);
pio_address += (pio_len_bytes & 0xfffe);
}
/*
* Deal with a trailing byte.
*/
if ((pio_len_bytes & 0x01) != 0)
{
sys_outsb(
adapter_handle,
pio_location,
*(pio_address)
);
}
}
/*
* Transfer done. We have to tell the hardware we have finished.
* This involves clearing the SHLDA bit to signal the end of PIO.
* Note that the SRESET bit is kept set otherwise we will reset the
* EAGLE.
*/
sys_outsb(
adapter_handle,
control_1,
PCMCIA_CTRL1_SRESET);
/*
* Poll until SHLAD clears.
*/
/*
* Now wait until SHLDA clears. This is needed since while SHLDA is
* set the EAGLE still believes it is in bus master mode; and any
* attempt to access the SIF will fail.
*/
do
{
dummy = sys_insb(adapter_handle, control_1);
/*
* It is possible that the adapter was removed during the
* transfer. If it was we could spin forever. We will test
* the value read from control_1, if it is 0xff then we
* assume that the adapter has been removed.
*/
if (dummy == 0xff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io( adapter);
#endif
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
}
while ((dummy & PCMCIA_CTRL1_SHLDA) != 0);
/*
* Finish off the software handshake process that we started at the
* beginning. This time we have to write 0xFFFF to
* DIO_LOCATION_DMA_CONTROL
* Do a read first - otherwise the write might fail
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
dummy = sys_insw(adapter_handle, sifdat);
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
sys_outsw(adapter_handle, sifdat, 0xFFFF);
sys_outsw(adapter_handle, sifadr, saved_sifadr);
/*
* acknowledge/clear interrupt at interrupt controller.
*/
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number);
#endif
/*
* Set interrupt enable bit to regenerate any lost interrupts.
*/
macro_setb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SINTREN);
}
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
}
#else
/*---------------------------------------------------------------------------
Ý
Ý This is the special interrupt handler for WinBooks.
Ý
Ý On a WinBook there is a 120us window after an interrupt has been
Ý signalled before the interrupt line floats up to a high enough
Ý level that another interrupt can be generated. To avoid loosing
Ý an interrupt in this window we must hold either not allow another
Ý interrupt to be generated in the windows or poll the adapter for
Ý interrupt causes for the duration of the window. Since on some
Ý OSs we cannot spend very long in our interrupt handler we cannot
Ý just poll (which is the fastest scheme) so we use a mixture. We
Ý use the PIO handshake to delay a second PIO interrupt and poll
Ý for SIF interrupts - which are much rarer.
Ý
---------------------------------------------------------------------------*/
export void
hwi_pcmcia_interrupt_handler(
ADAPTER * adapter
)
{
ADAPTER_HANDLE adapter_handle = adapter->adapter_handle;
WORD control_1 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_1;
WORD control_2 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_2;
WORD pio_location = adapter->io_location +
PCMCIA_PIO_IO_LOC;
WORD sifint_register = adapter->sif_int;
WORD sifadr = adapter->sif_adr;
WORD sifdat = adapter->sif_dat;
WORD sifacl = adapter->sif_acl;
WORD sifint_value;
WORD sifint_tmp;
BYTE FAR * pio_address;
WORD pio_len_bytes;
WORD pio_from_adapter;
WORD saved_sifadr;
WORD dummy;
DWORD dma_high_word;
WORD dma_low_word;
WORD temp_word;
UINT i;
#ifndef FTK_NO_IO_ENABLE
macro_enable_io( adapter);
#endif
/*
* Clear SIF interrupt enable bit in control register.
*/
macro_clearb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SINTREN);
/*
* Check for SIF interrupt or PIO interrupt. We do this 80 times
* as this has been empirically shown to result in a delay of
* 120us since we cleared SINTREN above.
*/
for (i = 0; i < 80; i++)
{
if ((sys_insw(adapter_handle, sifint_register) &
FASTMAC_SIFINT_IRQ_DRIVER) != 0)
{
/*
* A SIF interrupt has occurred. This could be an SRB free,
* an adapter check or a received frame interrupt.
*/
/*
* Clear EAGLE_SIFINT_HOST_IRQ to acknowledge interrupt at SIF.
*/
/*
* WARNING: Do NOT reorder the clearing of the SIFINT register with
* the reading of it. If SIFINT is cleared after reading it, any
* interrupts raised after reading it will be lost. Admittedly
* this is a small time frame, but it is important.
*/
sys_outsw(adapter_handle, sifint_register, 0);
/*
* Record the EAGLE SIF interrupt register value.
*/
/*
* WARNING: Continue to read the SIFINT register until it is stable
* because of a potential problem involving the host reading the
* register after the adapter has written the low byte of it, but
* before it has written the high byte. Failure to wait for the
* SIFINT register to settle can cause spurious interrupts.
*/
sifint_value = sys_insw(adapter_handle, sifint_register);
do
{
sifint_tmp = sifint_value;
sifint_value = sys_insw(adapter_handle, sifint_register);
}
while (sifint_tmp != sifint_value);
/*
* It is possible that the PCMCIA adapter may have been removed. In
* which case we would expect to get 0xffff from an IO location
* occupied by the adapter. We will check the value of SIFADR since
* this should never be 0xffff.
*/
if (sys_insw(adapter_handle, sifadr) == 0xffff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io( adapter);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
/*
* Call driver with details of SIF interrupt.
*/
driver_interrupt_entry(adapter_handle, adapter, sifint_value);
}
else if ((sys_insb(adapter_handle, control_1) & PCMCIA_CTRL1_SHRQ) != 0)
{
/*
* A PIO interrupt has occurred. Transfer data to/from adapter.
*/
saved_sifadr = sys_insw(adapter_handle, sifadr);
/*
* Read the physical address for the PIO through DIO space from the
* SIF registers. Because the SIF thinks that it is doing real DMA,
* the SDMAADR SDMAADX registers cannot be paged in, so they must
* be read from their memory mapped locations in Eagle memory.
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_EXT_DMA_ADDR);
dma_high_word = (DWORD)sys_insw(adapter_handle, sifdat);
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_ADDR);
dma_low_word = sys_insw(adapter_handle, sifdat);
pio_address = (BYTE FAR *) ((dma_high_word << 16) |
((DWORD) dma_low_word));
/*
* Read the PIO length from SIF register.
*/
pio_len_bytes = sys_insw(adapter_handle, adapter->sif_dmalen);
/*
* Determine what direction the data transfer is to take place in.
*/
pio_from_adapter = sys_insw(
adapter_handle,
sifacl) & EAGLE_SIFACL_SWDDIR;
/*
* It is possible that the PCMCIA adapter may have been removed. In
* which case we would expect to get 0xffff from an IO location
* occupied by the adapter. We will check the value of pio_len_bytes
* since this should never be 0xffff. We do this test here as we have
* read what we think are the DMA location and length. The following
* code may DMA rubbish if the adapter goes away after this test
* but at least we know it will happen to/from a valid host buffer.
*/
if (pio_len_bytes == 0xffff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number
);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
/*
* We need to use software handshaking across the PIO.
* Start by writing zero to a magic location on the adapter.
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
sys_outsw(adapter_handle, sifdat, 0);
/*
* Start what the SIF thinks is a DMA but is PIO instead. This
* involves clearing the SINTREN bit and setting SHLDA bit on
* control register 1. Note that we have to keep SRESET bit set
* otherwise we will reset the EAGLE.
*/
macro_setb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SHLDA);
/*
* Do the actual data transfer.
*/
/*
* Note that Fastmac only copies whole WORDs to DWORD boundaries
* FastmacPlus, however, can transfer any length to any address.
*/
if (pio_from_adapter)
{
/*
*
*
* Transfer into host memory from adapter.
*
*
*/
/*
* Deal with an odd leading byte.
*/
if (((card_t) pio_address & 0x01) != 0)
{
/*
* Read a WORD, the top byte is data.
*/
temp_word = sys_insw(adapter_handle, pio_location);
pio_len_bytes--;
*(pio_address++) = (BYTE) (temp_word >> 8);
}
/*
* We might be able to do 32 bit PIO.
*/
if (adapter->use_32bit_pio)
{
/*
* Deal with an odd leading word.
*/
if (((card_t) pio_address & 0x02) != 0 && pio_len_bytes > 0)
{
/*
* There could be one or two bytes of data.
*/
if (pio_len_bytes == 1)
{
pio_len_bytes--;
*(pio_address) =
sys_insb(adapter_handle, pio_location);
}
else
{
pio_len_bytes -= 2;
*((WORD *) pio_address) =
sys_insw(adapter_handle, pio_location);
pio_address += 2;
}
}
/*
* Deal with the bulk of the dwords.
*/
sys_rep_insd(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 2)
);
pio_address += (pio_len_bytes & 0xfffc);
/*
* Deal with a trailing word.
*/
if ((pio_len_bytes & 0x02) != 0)
{
/*
* Read a word.
*/
*((WORD *) pio_address) =
sys_insw(adapter_handle, pio_location);
pio_address += 2;
}
}
/*
* Otherwise use 16 bit PIO.
*/
else
{
/*
* Transfer the bulk of the data.
*/
sys_rep_insw(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 1)
);
pio_address += (pio_len_bytes & 0xfffe);
}
/*
* Finally transfer any trailing byte.
*/
if ((pio_len_bytes & 0x01) != 0)
{
*(pio_address) = sys_insb(adapter_handle, pio_location);
}
}
else
{
/*
*
*
* Transfer into adapter memory from the host.
*
*
*/
/*
* Deal with a leading odd byte.
*/
if (((card_t) pio_address & 0x01) != 0)
{
/*
* Write a WORD with data in top byte.
*/
temp_word = ((WORD) *(pio_address++)) << 8;
pio_len_bytes--;
sys_outsw(adapter_handle, pio_location, temp_word);
}
/*
* We might be able to do 32 bit PIO.
*/
if (adapter->use_32bit_pio)
{
/*
* Deal with an odd leading word.
*/
if (((card_t) pio_address & 0x02) != 0 && pio_len_bytes > 0)
{
/*
* There could be one or two bytes of data. If there
* is only one byte it goes in the high word.
*/
if (pio_len_bytes == 1)
{
pio_len_bytes--;
sys_outsb(
adapter_handle,
pio_location,
*(pio_address)
);
}
else
{
pio_len_bytes -= 2;
sys_outsw(
adapter_handle,
pio_location,
*((WORD *) pio_address)
);
pio_address += 2;
}
}
/*
* Deal with the bulk of the dwords.
*/
sys_rep_outsd(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 2)
);
pio_address += (pio_len_bytes & 0xfffc);
/*
* Deal with a trailing word.
*/
if ((pio_len_bytes & 0x02) != 0)
{
/*
* Write a word.
*/
sys_outsw(
adapter_handle,
pio_location,
*((WORD *) pio_address)
);
pio_address += 2;
}
}
/*
* Otherwise use 16 bit PIO.
*/
else
{
sys_rep_outsw(
adapter_handle,
pio_location,
pio_address,
(WORD) (pio_len_bytes >> 1)
);
pio_address += (pio_len_bytes & 0xfffe);
}
/*
* Deal with a trailing byte.
*/
if ((pio_len_bytes & 0x01) != 0)
{
sys_outsb(
adapter_handle,
pio_location,
*(pio_address)
);
}
}
/*
* Transfer done. We have to tell the hardware we have finished.
* This involves clearing the SHLDA bit to signal the end of PIO.
* Note that the SRESET bit is kept set otherwise we will reset the
* EAGLE.
*/
macro_clearb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SHLDA);
/*
* Poll until SHLDA clears.
*/
/*
* Now wait until SHLDA clears. This is needed since while SHLDA is
* set the EAGLE still believes it is in bus master mode; and any
* attempt to access the SIF will fail.
*/
do
{
dummy = sys_insb(adapter_handle, control_1);
/*
* It is possible that the adapter was removed during the
* transfer. If it was we could spin forever. We will test
* the value read from control_1, if it is 0xff then we
* assume that the adapter has been removed.
*/
if (dummy == 0xff)
{
#ifndef FTK_NO_IO_ENABLE
macro_disable_io( adapter);
#endif
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number);
#endif
#ifdef FTK_ADAPTER_REMOVED_NOTIFY
user_adapter_removed(adapter_handle);
#endif
return;
}
}
while ((dummy & PCMCIA_CTRL1_SHLDA) != 0);
/*
* We don't clear the handshake here to stop another
* PIO interrupt being generated for at least 120us
*/
}
}
/*
* Finish off the software handshake process that we started at the
* beginning. This time we have to write 0xFFFF to
* DIO_LOCATION_DMA_CONTROL
*
* Do a read first - otherwise the write might fail
*/
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
dummy = sys_insw(adapter_handle, sifdat);
sys_outsw(adapter_handle, sifadr, DIO_LOCATION_DMA_CONTROL);
sys_outsw(adapter_handle, sifdat, 0xFFFF);
sys_outsw(adapter_handle, sifadr, saved_sifadr);
/*
* acknowledge/clear interrupt at interrupt controller.
*/
#ifndef FTK_NO_CLEAR_IRQ
sys_clear_controller_interrupt(
adapter_handle,
adapter->interrupt_number);
#endif
/*
* Set interrupt enable bit to regenerate any lost interrupts.
*/
macro_setb_bit(adapter_handle, control_1, PCMCIA_CTRL1_SINTREN);
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
}
#endif
/****************************************************************************
*
* hwi_pcmcia_remove_card
* ======================
*
*
* PARAMETERS (passed by hwi_remove_adapter) :
* ===========================================
*
* ADAPTER * adapter
*
* This structure is used to identify and record specific information about
* the required adapter.
*
*
* BODY :
* ======
*
* The hwi_pcmcia_remove_card routine is called by hwi_remove_adapter. It
* disables interrupts if they are being used. It also resets the adapter.
* Note there is no need to explicitly disable DMA channels.
*
*
* RETURNS :
* =========
*
* The routine always successfully completes.
*
****************************************************************************/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_remove_card)
#endif
export void
hwi_pcmcia_remove_card(
ADAPTER * adapter
)
{
WORD sif_acontrol = adapter->sif_acl;
WORD control_1 = adapter->io_location +
PCMCIA_CONTROL_REGISTER_1;
/*
* Disable interrupts. Only need to do this if interrupts successfully
* enabled.
* Interrupts must be disabled at adapter before unpatching interrupts.
* Even in polling mode we must turn off interrupts at adapter.
*/
#ifndef FTK_NO_IO_ENABLE
macro_enable_io(adapter);
#endif
if (adapter->interrupts_on)
{
macro_clearw_bit(
adapter->adapter_handle,
sif_acontrol,
EAGLE_SIFACL_SINTEN);
#ifdef PCMCIA_POINT_ENABLE
/* Deconfigure and power down the card before disabling host
* interrupt handler in case of spurious interrupts from the
* PCMCIA controller.
*/
/*
* Warn our handler of the likelihood of a spurious interrupt.
*/
adapter->drop_int = TRUE;
sys_pcmcia_point_disable(adapter);
#endif
if (adapter->interrupt_number != POLLING_INTERRUPTS_MODE)
{
sys_disable_irq_channel(
adapter->adapter_handle,
adapter->interrupt_number);
}
adapter->interrupts_on = FALSE;
}
/*
* Perform adapter reset, and set EAGLE_SIFACL_ARESET high.
*/
macro_clearb_bit(
adapter->adapter_handle,
control_1,
PCMCIA_CTRL1_SRESET);
macro_setw_bit(
adapter->adapter_handle,
sif_acontrol,
EAGLE_SIFACL_ARESET);
#ifndef FTK_NO_IO_ENABLE
macro_disable_io(adapter);
#endif
}
/****************************************************************************
*
* hwi_pcmcia_set_dio_address
* ==========================
*
* The hwi_pcmcia_set_dio_address routine is used, with pcmcia cards, for
* putting a 32 bit DIO address into the SIF DIO address and extended DIO
* address registers.
*
****************************************************************************/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(hwi_pcmcia_set_dio_address)
#endif
export void
hwi_pcmcia_set_dio_address(
ADAPTER * adapter,
DWORD dio_address
)
{
WORD sif_dio_adr = adapter->sif_adr;
WORD sif_dio_adrx = adapter->sif_adx;
/*
* Load extended DIO address register with top 16 bits of address.
* Always load extended address register first.
* Note pcmcia cards have single page of all SIF registers hence do not
* need page in certain SIF registers.
*/
sys_outsw(
adapter->adapter_handle,
sif_dio_adrx,
(WORD)(dio_address >> 16));
/*
* Load DIO address register with low 16 bits of address.
*/
sys_outsw(
adapter->adapter_handle,
sif_dio_adr,
(WORD)(dio_address & 0x0000FFFF));
}
/*---------------------------------------------------------------------------
|
| LOCAL PROCEDURES
|
---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
|
| pcmcia_c46_write_bit
| ====================
|
| Write a bit to the SEEPROM control register.
|
---------------------------------------------------------------------------*/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(pcmcia_c46_write_bit)
#endif
local void
pcmcia_c46_write_bit(
ADAPTER * adapter,
WORD mask,
WBOOLEAN set_bit)
{
WORD ctrl_reg;
/*
* Get the current value of the SEEPROM control register.
*/
ctrl_reg = sys_insb(
adapter->adapter_handle,
(WORD) (adapter->io_location + PCMCIA_CONTROL_REGISTER_2)
);
/*
* Clear or set the bit.
*/
if (set_bit)
{
ctrl_reg |= mask;
}
else
{
ctrl_reg &= ~mask;
}
/*
* Write the data to the SEEPROM control register.
*/
sys_outsb(
adapter->adapter_handle,
(WORD) (adapter->io_location + PCMCIA_CONTROL_REGISTER_2),
(BYTE) ctrl_reg);
/*
* Wait for a bit.
*/
sys_wait_at_least_microseconds(10);
/*
* And read the SEEPROM control register back so that the data gets
* latched properly.
*/
sys_insb(
adapter->adapter_handle,
(WORD) (adapter->io_location + PCMCIA_CONTROL_REGISTER_2));
}
/*---------------------------------------------------------------------------
|
| pcmcia_c46_twitch_clock
| =======================
|
| Toggle the SEEPROM clock.
|
---------------------------------------------------------------------------*/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(pcmcia_c46_twitch_clock)
#endif
local void
pcmcia_c46_twitch_clock(
ADAPTER * adapter
)
{
pcmcia_c46_write_bit(adapter, PCMCIA_CTRL2_E2SK, TRUE);
pcmcia_c46_write_bit(adapter, PCMCIA_CTRL2_E2SK, FALSE);
}
/*---------------------------------------------------------------------------
|
| pcmcia_c46_read_data
| ====================
|
| Read a data bit from the SEEPROM control register
|
---------------------------------------------------------------------------*/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(pcmcia_c46_read_data)
#endif
local WORD
pcmcia_c46_read_data(
ADAPTER * adapter
)
{
return sys_insb(
adapter->adapter_handle,
(WORD) (adapter->io_location + PCMCIA_CONTROL_REGISTER_2)
) & PCMCIA_CTRL2_E2DO;
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_read_eeprom_word
| ===========================
|
| hwi_pcmcia_read_eeprom_word takes the address of the word to be read
| from the AT93C46 serial EEPROM, write to the IO ports a magic sequence
| to switch the EEPROM to reading mode and finally read the required word
| and return.
|
---------------------------------------------------------------------------*/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(hwi_pcmcia_read_eeprom_word)
#endif
local WORD
hwi_pcmcia_read_eeprom_word(
ADAPTER * adapter,
WORD word_address
)
{
WORD i;
WORD cmd_word = C46_START_BIT | C46_READ_CMD;
WORD tmp_word;
/*
* Concatenate the address to command word.
*/
cmd_word |= (WORD)((word_address & C46_ADDR_MASK) << C46_ADDR_SHIFT);
/*
* Clear data in bit.
*/
pcmcia_c46_write_bit(
adapter,
PCMCIA_CTRL2_E2DI,
FALSE);
/*
* Assert chip select bit.
*/
pcmcia_c46_write_bit(
adapter,
PCMCIA_CTRL2_E2CS,
TRUE);
/*
* Send read command and address.
*/
pcmcia_c46_twitch_clock(
adapter);
tmp_word = cmd_word;
for (i = 0; i < C46_CMD_LENGTH; i++)
{
pcmcia_c46_write_bit(
adapter,
PCMCIA_CTRL2_E2DI,
(WBOOLEAN) ((tmp_word & 0x8000) != 0));
pcmcia_c46_twitch_clock(adapter);
tmp_word <<= 1;
}
/*
* Read data word.
*/
tmp_word = 0x0000;
for (i = 0; i < 16; i++)
{
pcmcia_c46_twitch_clock(adapter);
if (i > 0)
{
tmp_word <<= 1;
}
if (pcmcia_c46_read_data(adapter) != 0)
{
tmp_word |= 0x0001;
}
}
/*
* Clear data in bit.
*/
pcmcia_c46_write_bit(
adapter,
PCMCIA_CTRL2_E2DI,
FALSE);
/*
* Deselect chip.
*/
pcmcia_c46_write_bit(
adapter,
PCMCIA_CTRL2_E2CS,
FALSE);
/*
* Tick clock.
*/
pcmcia_c46_twitch_clock(adapter);
return tmp_word;
}
#ifndef FTK_NO_PROBE
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_card_services_setup
| ==============================
|
| hwi_pcmcia_card_services_setup is called by hwi_pcmcia_install_card. It
| handles all the procedures required to register with PCMCIA Card
| Serivces and request I O and interrupt resources.
|
| The caller must fill in interrupt_number and io_location field of the
| input adapter structure. Interrupt number can be any number supported by
| the 16 4 PCMCIA ringnode or DETERMINED_BY_CS. A valid io_location mus
| be supplied.
|
---------------------------------------------------------------------------*/
#ifdef FTK_INIT_FUNCTION
#pragma FTK_INIT_FUNCTION(hwi_pcmcia_card_services_setup)
#endif
local WBOOLEAN
hwi_pcmcia_card_services_setup(
PROBE * resource
)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD rc;
WORD ClientHandle;
DWORD i = 0;
WORD j;
void FAR * CallBackPtr = (void FAR *) Callback;
WORD socket = DETERMINED_BY_CARD_SERVICES;
WORD irq = DETERMINED_BY_CARD_SERVICES;
WORD io_location = resource->io_location;
rc = CardServices(
CS_GetCardServicesInfo, /* Function */
NULL, /* Handle not used */
NULL, /* Pointer not used */
MAX_PCMCIA_ARG_BUFFER_LEN, /* argbuffer length */
fp ); /* argbuffer */
if (rc == CMD_SUCCESS)
{
CS_GET_CS_INFO_ARG FAR * ptr = (CS_GET_CS_INFO_ARG FAR *)fp;
if ((ptr->Signature[0] == 'C') &&
(ptr->Signature[1] == 'S'))
{
CardServicesVersion = ptr->CSLevel;
}
else if ((ptr->Signature[0] == 'M') &&
(ptr->Signature[1] == 'N'))
{
CardServicesVersion = ptr->CSLevel;
}
else
{
return FALSE;
}
}
else
{
return FALSE;
}
{
CS_REGISTER_CLIENT_ARG FAR * ptr =
(CS_REGISTER_CLIENT_ARG FAR *)fp;
ptr->Attributes = RC_ATTR_IO_CLIENT_DEVICE_DRIVER |
RC_ATTR_IO_INSERTION_SHARABLE;
ptr->EventMask = MASK_CARD_DETECT;
ptr->ClientData[0] = 0x00; /* Not used */
ptr->ClientData[1] = 0x00; /* CardServices will put DS here. */
ptr->ClientData[2] = 0x00; /* Not used */
ptr->ClientData[3] = 0x00; /* Not used */
ptr->Version = 0x0201;
}
/*
* Set RegisterClientCompleted flag to FALSE. When registration
* complete event occur, call back function will set this flag to TRUE.
*/
RegisterClientCompleted = FALSE;
rc = CardServices(
CS_RegisterClient,
(WORD FAR *)&ClientHandle,
(void * FAR *)&CallBackPtr,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
if (CMD_SUCCESS != rc)
{
return FALSE;
}
while (!RegisterClientCompleted)
{
/*
* Wait for card insertion event to complete, timeout if waiting
* for too long.
*/
i++;
if (i == PCMCIA_CS_REGISTRATION_TIMEOUT)
{
return FALSE;
}
}
/*
* Check if there is a least one socket ready for use. Return false if
* none is found. Remember we have registered with card services, so
* deregister before returning.
*/
for (j = 0; j < MAX_PCMCIA_SOCKETS; j++)
{
if (SOCKET_READY == pcmcia_socket_table[j].status)
{
break;
}
}
if (MAX_PCMCIA_SOCKETS == j)
{
/*
* No socket available. Either no Madge card installed or they are
* already in use. Report error here. Must deregister before
* returning.
*/
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
if (socket != DETERMINED_BY_CARD_SERVICES)
{
/*
* User specified a socket number to use. Check if it is available.
*/
if (pcmcia_socket_table[socket].status == SOCKET_NOT_INITIALIZED)
{
/*
* No Madge Card found in PCMCIA socket specified, deregister
* and report error.
*/
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
else if (pcmcia_socket_table[socket].status == SOCKET_IN_USE)
{
/*
* Card in socket number specified is currently in use,
* deregister and report error.
*/
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
else
{
/*
* Socket number specified has a Madge Card in it and it is
* available.
*/
;
}
}
else
{
/*
* User asked Card Services to choose which socket to use.
* Use the first available one.
*/
socket = j;
}
/*
* Call CS AdjustResourceInfo.
*/
{
CS_ADJ_IO_RESOURCE_ARG FAR * ptr =
(CS_ADJ_IO_RESOURCE_ARG FAR *)fp;
ptr->Action = ARI_ACTION_ADD;
ptr->Resource = ARI_RESOURCE_IO;
ptr->BasePort = io_location;
ptr->NumPorts = PCMCIA_IO_RANGE;
ptr->Attributes = 0x00;
ptr->IOAddrLines = PCMCIA_NUMBER_OF_ADDR_LINES;
}
rc = CardServices(CS_AdjustResourceInfo,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
if (CMD_SUCCESS != rc)
{
/*
* Requested IO location is not available, deregister and report
* error.
*/
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
/*
* AdjustResourceInfo does not exist for CS V2.00 on the IBM ThinkPad.
* Assume it is ok. Do not bother to check the return code here.
*/
/*
* Call CS RequestIO to ask for IO resources.
*/
{
CS_REQUEST_IO_ARG FAR * ptr = (CS_REQUEST_IO_ARG FAR *) fp;
ptr->Socket = socket;
ptr->BasePort1 = io_location;
ptr->NumPorts1 = PCMCIA_IO_RANGE;
ptr->Attributes1 = RIO_ATTR_16_BIT_DATA;
ptr->BasePort2 = 0x0000;
ptr->NumPorts2 = 0x00;
ptr->Attributes2 = 0x00;
ptr->IOAddrLines = PCMCIA_NUMBER_OF_ADDR_LINES;
}
rc = CardServices(CS_RequestIO,
(WORD FAR *)&ClientHandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
if (CMD_SUCCESS != rc)
{
/*
* Requested IO location is not available, deregister and report
* error.
*/
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
/*
* Call CS RequestIRQ to ask for interrupt resources.
*/
{
CS_REQUEST_IRQ_ARG FAR * ptr = (CS_REQUEST_IRQ_ARG FAR *)fp;
if (DETERMINED_BY_CARD_SERVICES == irq)
{
/*
* User asked card services to choose any interrupt channel
* available.
*/
ptr->IRQInfo1 = PCMCIA_IRQINFO1_NOT_SPECIFIED;
/*
* List of IRQ channel available (in form of bit mask) for card
* services to choose from. Exclude those already in use.
*/
ptr->IRQInfo2 = PCMCIA_AVAILABLE_IRQ_MASK & ~UsedIrqChannelsMask;
}
else if ((0x0001 << irq) & PCMCIA_AVAILABLE_IRQ_MASK)
{
/*
* Use the interrupt channel the user supplied.
*/
ptr->IRQInfo1 = (BYTE)((irq & 0x00FF) |
PCMCIA_IRQINFO1_SPECIFIED);
ptr->IRQInfo2 = 0x0000;
}
else
{
/*
* An invalid interrupt channel is specified.
* We have already requested for an IO resource, we must
* release it and deregister with card services before
* returning.
*/
hwi_pcmcia_cs_release_io(ClientHandle, socket, io_location);
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
ptr->Socket = socket;
ptr->Attributes = 0x0000;
rc = CardServices(
CS_RequestIRQ,
(WORD FAR *)&ClientHandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
if (CMD_SUCCESS == rc)
{
/*
* Card Services successfully allocated us an interrupt
* channel, record it for later use.
*/
irq = (WORD)ptr->AssignedIRQ;
}
else
{ /*
* Failed to obtain an interrupt channel. Release the IO
* allocated and deregister with card services.
*/
hwi_pcmcia_cs_release_io(ClientHandle, socket, io_location);
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
}
/*
* Call CS RequestConfiguration.
*/
{
CS_REQUEST_CONFIG_ARG FAR * ptr = (CS_REQUEST_CONFIG_ARG FAR *)fp;
ptr->Socket = socket;
ptr->Attributes = RC_ATTR_ENABLE_IRQ_STEERING;
ptr->Vcc = PCMCIA_VCC;
ptr->Vpp1 = PCMCIA_VPP1;
ptr->Vpp2 = PCMCIA_VPP2;
ptr->IntType = RC_INTTYPE_MEMORY_AND_IO;
ptr->ConfigBase = PCMCIA_CONFIG_BASE;
ptr->Status = PCMCIA_STATUS_REG_SETTING;
ptr->Pin = PCMCIA_PIN_REG_SETTING;
ptr->Copy = PCMCIA_COPY_REG_SETTING;
ptr->ConfigIndex = PCMCIA_OPTION_REG_SETTING;
ptr->Present = PCMCIA_REGISTER_PRESENT;
}
rc = CardServices(
CS_RequestConfiguration,
(WORD FAR *)&ClientHandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
if (CMD_SUCCESS != rc)
{
/*
* RequestConfiguration failed. Release all resources and return
* error.
*/
hwi_pcmcia_cs_release_io(ClientHandle, socket, io_location);
hwi_pcmcia_cs_release_irq(ClientHandle, socket, irq);
hwi_pcmcia_cs_deregister_client(ClientHandle);
return FALSE;
}
/*
* We successfully made all the card services call. Update our local
* sockets record here.
*/
pcmcia_socket_table[socket].status = SOCKET_IN_USE;
pcmcia_socket_table[socket].irq_number = irq;
pcmcia_socket_table[socket].io_location = io_location;
pcmcia_socket_table[socket].ClientHandle = ClientHandle;
/*
* Record interrupt channel used.
*/
UsedIrqChannelsMask |= ( 0x0001 << irq );
/*
* Fill adapter sturcture with resources allocated.
*/
resource->interrupt_number = irq;
resource->io_location = io_location;
resource->socket = socket;
/*
* Toggle the top bit of Configuration Option Register to reset card.
*/
/*
* Note that this is not necessary for later cards.
* CS 2.00 may not have this function, so just do it but don't bother
* to check return code.
*/
{
CS_ACCESS_CONFIG_REG_ARG FAR * ptr =
(CS_ACCESS_CONFIG_REG_ARG FAR *) fp;
ptr->Socket = socket;
ptr->Action = ACR_ACTION_WRITE;
ptr->Offset = PCMCIA_OPTION_REG;
ptr->Value = PCMCIA_COR_SYSRESET;
CardServices(
CS_AccessConfigurationRegister,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
ptr->Value = 0x00;
CardServices(
CS_AccessConfigurationRegister,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
ptr->Value = PCMCIA_OPTION_REG_SETTING;
CardServices(
CS_AccessConfigurationRegister,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp );
}
return TRUE;
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_cs_release_config
| ============================
|
| hwi_pcmcia_cs_release_config releases resources requested earlier from
| Card Services. This is part of the shut down procedure.
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_cs_release_config)
#endif
local WORD
hwi_pcmcia_cs_release_config(
WORD ClientHandle,
WORD Socket)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD chandle = ClientHandle;
CS_RELEASE_CONFIG_ARG FAR * ptr = (CS_RELEASE_CONFIG_ARG FAR *)fp;
ptr->Socket = Socket;
return CardServices(
CS_ReleaseConfiguration,
&chandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp);
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_cs_release_io
| ========================
|
| hwi_pcmcia_cs_release_io releases the IO resources requested earlier
| from Card Services.
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_cs_release_io)
#endif
local WORD
hwi_pcmcia_cs_release_io(
WORD ClientHandle,
WORD Socket,
WORD IoLocation
)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD chandle = ClientHandle;
CS_RELEASE_IO_ARG FAR * ptr = (CS_RELEASE_IO_ARG FAR *)fp;
ptr->Socket = Socket;
ptr->BasePort1 = IoLocation;
ptr->NumPorts1 = PCMCIA_IO_RANGE;
ptr->Attributes1 = RIO_ATTR_16_BIT_DATA;
ptr->BasePort2 = 0x0000;
ptr->NumPorts2 = 0x00;
ptr->Attributes2 = 0x00;
ptr->IOAddrLines = PCMCIA_NUMBER_OF_ADDR_LINES;
return CardServices(
CS_ReleaseIO,
(WORD FAR *)&chandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp);
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_cs_release_irq
| =========================
|
| hwi_pcmcia_cs_release_irq release the IRQ resources requested earlier
| from Card Services.
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_cs_release_irq)
#endif
local WORD
hwi_pcmcia_cs_release_irq(
WORD ClientHandle,
WORD Socket,
WORD IRQChannel)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD chandle = ClientHandle;
CS_RELEASE_IRQ_ARG FAR * ptr = (CS_RELEASE_IRQ_ARG FAR *)fp;
ptr->Socket = Socket;
ptr->Attributes = 0x0000;
ptr->AssignedIRQ = (BYTE)IRQChannel;
return CardServices(
CS_ReleaseIRQ,
(WORD FAR *)&chandle,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp);
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_cs_deregister_client
| ===============================
|
| hwi_pcmcia_cs_deregister_client informs PCMCIA card services that we are
| not longer interest in any PCMCIA event and/or resources. It is called
| in shut down or failure in invoking other card services related
| function.
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_cs_deregister_client)
#endif
local WORD
hwi_pcmcia_cs_deregister_client(
WORD ClientHandle
)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD chandle = ClientHandle;
return CardServices(
CS_DeregisterClient,
(WORD FAR *)&chandle,
NULL,
0,
fp);
}
/*---------------------------------------------------------------------------
|
| hwi_pcmcia_tear_down_cs
| =======================
|
| hwi_pcmcia_tear_down_cs is called by hwi_pcmcia_remove card to release
| all the resources allocated by PCMCIA card services and deregister with
| card services
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(hwi_pcmcia_tear_down_cs)
#endif
local WBOOLEAN
hwi_pcmcia_tear_down_cs(
PROBE resource
)
{
WORD socket = resource.socket;
WORD ClientHandle;
WORD io_location;
WORD irq_number;
WBOOLEAN rc1;
WBOOLEAN rc2;
WBOOLEAN rc3;
WBOOLEAN rc4;
if (pcmcia_socket_table[socket].status != SOCKET_IN_USE)
{
return FALSE;
}
/*
* Socket number found. retrieve clienthandle, irq, iolocation from our
* local socket record.
*/
ClientHandle = pcmcia_socket_table[socket].ClientHandle;
io_location = pcmcia_socket_table[socket].io_location;
irq_number = pcmcia_socket_table[socket].irq_number;
/*
* Call the PCMCIA card services routine to bring it down.
*/
rc1 = hwi_pcmcia_cs_release_config(ClientHandle, socket);
rc2 = hwi_pcmcia_cs_release_io(ClientHandle, socket, io_location);
rc3 = hwi_pcmcia_cs_release_irq(ClientHandle, socket, irq_number);
rc4 = hwi_pcmcia_cs_deregister_client(ClientHandle);
/*
* Update local record.
*/
pcmcia_socket_table[socket].ClientHandle = 0x0000;
pcmcia_socket_table[socket].io_location = 0x0000;
pcmcia_socket_table[socket].irq_number = 0x0000;
pcmcia_socket_table[socket].status = SOCKET_NOT_INITIALIZED;
return (rc1 && rc2 && rc3 && rc4);
}
/*---------------------------------------------------------------------------
|
| This is the callback function used by Card Services to notify client any
| event changes. Its prototype is defined in sys_cs.h module.
|
---------------------------------------------------------------------------*/
#ifdef FTK_RES_FUNCTION
#pragma FTK_RES_FUNCTION(Callback)
#endif
WORD FAR
Callback(
WORD Function,
WORD Socket,
WORD Info,
void FAR * MTDRequest,
void FAR * Buffer,
WORD Misc,
WORD ClientData1,
WORD ClientData2,
WORD ClientData3
)
{
NEW_PCMCIA_ARG_BUFFER(fp);
WORD rc;
switch (Function)
{
case REGISTRATION_COMPLETE :
RegisterClientCompleted = TRUE;
break;
case CARD_INSERTION :
/*
* Call GetFistTuple of card services.
*/
{
CS_GET_FIRST_TUPLE_ARG FAR * ptr =
(CS_GET_FIRST_TUPLE_ARG FAR *)fp;
ptr->Socket = Socket;
ptr->Attributes = 0x0000;
ptr->DesiredTuple = CISTPL_VERS_1;
ptr->Reserved = 0x00;
}
rc = CardServices(
CS_GetFirstTuple,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp);
if (rc != CMD_SUCCESS)
{
break;
}
/*
* Call GetTupleData of card services.
*/
{
CS_GET_TUPLE_DATA_ARG FAR * ptr =
(CS_GET_TUPLE_DATA_ARG FAR *)fp;
ptr->TupleOffset = 0x00;
ptr->TupleDataMax = MAX_PCMCIA_ARG_BUFFER_LEN;
}
rc = CardServices(
CS_GetTupleData,
NULL,
NULL,
MAX_PCMCIA_ARG_BUFFER_LEN,
fp);
if (rc != CMD_SUCCESS)
{
break;
}
/*
* Is it a Madge Smart 16/4 PCMCIA Ringnode?
*/
{
CS_GET_TUPLE_DATA_ARG FAR * ptr =
(CS_GET_TUPLE_DATA_ARG FAR *)fp;
BYTE FAR * info_ptr;
WORD i;
/*
* Find the signature strings in the tuple.
* Allow for CS version 2.00 or below. See the TupleData
* union in the header file.
*/
if (CardServicesVersion <= 0x0200)
{
info_ptr = ((CS200_CISTPL_VERS_1_DATA FAR *)
(ptr->TupleData) )->info;
}
else
{
info_ptr = ((CS201_CISTPL_VERS_1_DATA FAR *)
(ptr->TupleData) )->info;
}
/*
* Compare signature strings. Avoid strcmp, _fstrcmp,
* memcmp, _fmemcmp, etc. to make it model independent.
*/
for (i = 0; i < MADGE_TPLLV1_INFO_LEN; i++)
{
if (*(MADGE_TPLLV1_INFO+i) != *(info_ptr+i))
{
break;
}
}
if (MADGE_TPLLV1_INFO_LEN == i) /* yes, a madge card. */
{
if (pcmcia_socket_table[Socket].status ==
SOCKET_NOT_INITIALIZED)
{
pcmcia_socket_table[Socket].status = SOCKET_READY;
}
/*
* do nothing if status is SOCKET_READY or
* SOCKET_IN_USE.
*/
}
}
break;
case CLIENT_INFO :
{
WORD buffer_size =
((CS_CLIENT_INFO FAR *)Buffer)->MaxLen;
WORD len_to_copy;
WORD i;
WORD madge_info_size = sizeof(MADGE_CLIENT_INFO);
BYTE FAR * src;
BYTE FAR * dest;
/*
* Copy whole client info structure if there is space,
* otherwise just fill the buffer supplied.
*/
len_to_copy =
(buffer_size > madge_info_size) ? madge_info_size :
buffer_size;
src = (BYTE FAR *)&default_client_info;
dest = (BYTE FAR *)Buffer;
for (i = 0; i < len_to_copy; i++)
{
if (i > 1) /* Skip MaxLen field which is preserved. */
{
*(dest+i) = *(src+1);
}
}
}
break;
case CARD_REMOVAL:
/*
* Only remove card in use.
* Check Socket < MAX_PCMCIA_SOCKETS to prevent access
* to out-of-bound array element.
*/
if ((Socket < MAX_PCMCIA_SOCKETS) &&
(pcmcia_socket_table[Socket].status == SOCKET_IN_USE))
{
WORD ClientHandle = pcmcia_socket_table[Socket].ClientHandle;
WORD io_location = pcmcia_socket_table[Socket].io_location;
WORD irq_number = pcmcia_socket_table[Socket].irq_number;
/*
* Call the PCMCIA card services routine to bring it down.
*/
hwi_pcmcia_cs_release_config(ClientHandle, Socket);
hwi_pcmcia_cs_release_io(ClientHandle, Socket, io_location);
hwi_pcmcia_cs_release_irq(ClientHandle, Socket, irq_number);
hwi_pcmcia_cs_deregister_client(ClientHandle);
/*
* Update local record.
*/
pcmcia_socket_table[Socket].ClientHandle = 0x0000;
pcmcia_socket_table[Socket].io_location = 0x0000;
pcmcia_socket_table[Socket].irq_number = 0x0000;
pcmcia_socket_table[Socket].status =
SOCKET_NOT_INITIALIZED;
pcmcia_socket_table[Socket].adapter_handle = 0x0000;
}
break;
default :
break;
}
return CMD_SUCCESS;
}
#endif
#endif
/******** End of HWI_PCMC.C ************************************************/