diff options
Diffstat (limited to 'private/ntos/ndis/madge/driver/hwi_pcmc.c')
-rw-r--r-- | private/ntos/ndis/madge/driver/hwi_pcmc.c | 3366 |
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 ************************************************/ |