mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3366 lines
94 KiB
3366 lines
94 KiB
/****************************************************************************
|
|
*
|
|
* 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 ************************************************/
|