summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/madge/driver/hwi_pcmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ndis/madge/driver/hwi_pcmc.c')
-rw-r--r--private/ntos/ndis/madge/driver/hwi_pcmc.c3366
1 files changed, 3366 insertions, 0 deletions
diff --git a/private/ntos/ndis/madge/driver/hwi_pcmc.c b/private/ntos/ndis/madge/driver/hwi_pcmc.c
new file mode 100644
index 000000000..f5aa76c3a
--- /dev/null
+++ b/private/ntos/ndis/madge/driver/hwi_pcmc.c
@@ -0,0 +1,3366 @@
+/****************************************************************************
+*
+* 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 ************************************************/