
//   vim:tw=110:ts=4:
/************************************************************************************************************
*
* FILE   :	HCF.C
*
* DATE    :	$Date: 2004/03/18 16:36:14 $   $Revision: 1.368 $
* Original:	2004/03/18 16:36:14    Revision: 1.368      Tag: t20040318_02
* Original:	2004/03/18 09:34:56    Revision: 1.367      Tag: t20040318_01
*
* AUTHOR :	Nico Valster
*
* SPECIFICATION: 025726 rev B
*
* DESCRIPTION :	HCF Routines for Hermes-I and Hermes-II (callable via the Wireless Connection I/F or WCI)
*				Local Support Routines for above procedures
*
*			Customizable via HCFCFG.H, which is included by HCF.H
*
*************************************************************************************************************
 *
 *
 * SOFTWARE LICENSE
 *
 * This software is provided subject to the following terms and conditions,
 * which you should read carefully before using the software.  Using this
 * software indicates your acceptance of these terms and conditions.  If you do
 * not agree with these terms and conditions, do not use the software.
 *
 * COPYRIGHT © 1994 - 1995	by AT&T.				All Rights Reserved
 * COPYRIGHT © 1996 - 2000 by Lucent Technologies.	All Rights Reserved
 * COPYRIGHT © 2001 - 2004	by Agere Systems Inc.	All Rights Reserved
 * All rights reserved.
 *
 * Redistribution and use in source or binary forms, with or without
 * modifications, are permitted provided that the following conditions are met:
 *
 * . Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following Disclaimer as comments in the code as
 *    well as in the documentation and/or other materials provided with the
 *    distribution.
 *
 * . Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following Disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * . Neither the name of Agere Systems Inc. nor the names of the contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * Disclaimer
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
 * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
 * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 *
************************************************************************************************************/


/************************************************************************************************************
**
** Implementation Notes
**
* -	a leading marker of //! is used. The purpose of such a sequence is to help to understand the flow
*	An example is:	//!rc = HCF_SUCCESS;
*	if this is superfluous because rc is already guaranteed to be 0 but it shows to the (maintenance)
*	programmer it is an intentional omission at the place where someone could consider it most appropriate at
*	first glance
* -	using near pointers in a model where ss!=ds is an invitation for disaster, so be aware of how you specify
*	your model and how you define variables which are used at interrupt time
* -	remember that sign extension on 32 bit platforms may cause problems unless code is carefully constructed,
*	e.g. use "(hcf_16)~foo" rather than "~foo"
*
************************************************************************************************************/

#include "hcf.h"				// HCF and MSF common include file
#include "hcfdef.h"				// HCF specific include file
#include "mmd.h"				// MoreModularDriver common include file
#if (HCF_TYPE) & HCF_TYPE_USB
wci_recordp usb_simp;			// "simulation" pointer for USB
#if (HCF_TYPE) & HCF_TYPE_HII
#include "..\wl_ucf\uhs_com.h"	// USB specific include file
#else
#include "..\wl_usb\unuhcf.h"	// USB specific include file
#endif // HCF_TYPE_HII
#endif // HCF_TYPE_USB

#if ! defined offsetof
#define offsetof(s,m)   (unsigned int)&(((s *)0)->m)
#endif // offsetof

/***********************************************************************************************************/
/***************************************  PROTOTYPES  ******************************************************/
/***********************************************************************************************************/
HCF_STATIC int			cmd_exe( IFBP ifbp, hcf_16 cmd_code, int par_0 );
HCF_STATIC int			init( IFBP ifbp );
#if HCF_MB_ON
HCF_STATIC void			put_info_mb( IFBP ifbp, hcf_16 type, wci_recordp bufp, hcf_16 len );
#endif // HCF_MB_ON
HCF_STATIC void			send_msg_frag( IFBP ifbp, wci_bufp bufp, int len );
HCF_STATIC void			send_mic_dcp( IFBP ifbp );
HCF_STATIC int			strio_32( IFBP, hcf_16 type, hcf_16 fid, hcf_16 offset, wci_recordp pc_addr, int len \
						   BE_PAR(int wlen) );
HCF_STATIC int			strio_16( IFBP, hcf_16 type, hcf_16 fid, hcf_16 offset, wci_recordp pc_addr, int len \
						   BE_PAR(int wlen) );
HCF_STATIC void			update_mic( hcf_32* p, hcf_32 M );
HCF_STATIC void			update_mic_dummy( hcf_32* p, hcf_32 M );

#if (HCF_TYPE) & HCF_TYPE_USB
#if ((HCF_TYPE) & HCF_TYPE_HII)
#if HCF_DLV //;?currently not supported || HCF_DLNV
HCF_STATIC int			download_ubsh2( IFBP ifbp, CFG_PROG_STRCT FAR *ltvp );
#endif // HCF_DLV / HCF_DLNV
#else
HCF_STATIC int			check_comp_usb( IFBP ifbp );
int					get_info_usb( IFBP ifbp, LTVP ltvp );
#endif // HCF_TYPE_HII
HCF_STATIC int			put_usb_nic_cmd( IFBP ifbp, hcf_16 cmd, hcf_16 par0, hcf_16 par1, hcf_16 par2 );
#else
HCF_STATIC int			aux_cntl( IFBP ifbp, hcf_16 cmd );						//non HCF_TYPE_HII only
HCF_STATIC void			calibrate( IFBP ifbp );
HCF_STATIC int			check_mic( IFBP ifbp );
HCF_STATIC int			cmd_cmpl( IFBP ifbp );
HCF_STATIC hcf_16		get_fid( IFBP ifbp );
HCF_STATIC void			isr_info( IFBP ifbp );
HCF_STATIC int			llb( IFBP ifbp, wci_bufp bufp );
HCF_STATIC int			put_info( IFBP ifbp, LTVP ltvp );
HCF_STATIC void			rcv_msg_frag( IFBP ifbp, wci_bufp bufp, int len );
#if HCF_DLV || HCF_DLNV
HCF_STATIC int			download( IFBP ifbp, CFG_PROG_STRCT FAR *ltvp );
HCF_STATIC void			download_data( IFBP ifbp, CFG_PROG_STRCT* ltvp, hcf_16 offset, hcf_16 page );
#endif // HCF_DLV / HCF_DLNV
#endif // HCF_TYPE_USB

#if HCF_ENCAPSULATION == 0x0001
HCF_STATIC hcf_8		hcf_encap( wci_bufp type );
#endif // HCF_ENCAPSULATION




/************************************************************************************************************
******************************* D A T A    D E F I N I T I O N S ********************************************
************************************************************************************************************/

#if HCF_ASSERT
IFBP BASED assert_ifbp = NULL;			//to make asserts easily work under MMD and DHF
#endif // HCF_ASSERT

#if HCF_ENCAPSULATION == 0x0001
/* SNAP header to be inserted in Ethernet-II frames */
HCF_STATIC  hcf_8 BASED snap_header[] = { 0xAA, 0xAA, 0x03, 0x00, 0x00,	//5 bytes signature +
									  0 };							//1 byte protocol identifier
#endif // HCF_ENCAPSULATION

HCF_STATIC hcf_8 BASED mic_pad[8] = { 0x5A, 0, 0, 0, 0, 0, 0, 0 };		//MIC padding of message

#if defined MSF_COMPONENT_ID
CFG_IDENTITY_STRCT BASED cfg_drv_identity = {
	sizeof(cfg_drv_identity)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_IDENTITY,			// (0x0826)
	MSF_COMPONENT_ID,
	MSF_COMPONENT_VAR,
	MSF_COMPONENT_MAJOR_VER,
	MSF_COMPONENT_MINOR_VER
} ;

CFG_RANGES_STRCT BASED cfg_drv_sup_range = {
	sizeof(cfg_drv_sup_range)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_SUP_RANGE,			// (0x0827)

	COMP_ROLE_SUPL,
	COMP_ID_DUI,
	{	DUI_COMPAT_VAR,
		DUI_COMPAT_BOT,
		DUI_COMPAT_TOP
	}
} ;

struct CFG_RANGE3_STRCT BASED cfg_drv_act_ranges_pri = {
	sizeof(cfg_drv_act_ranges_pri)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_PRI,		// (0x0828)

	COMP_ROLE_ACT,
	COMP_ID_PRI,
	{
#if defined HCF_PRI_VAR_1
	 {	1,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_PRI_1_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_PRI_1_TOP			//		 - Top Compatibility
	 },
#else // HCF_PRI_VAR_1
	 { 0, 0, 0 },
#endif // HCF_PRI_VAR_1
#if defined HCF_PRI_VAR_2
	 {	2,									//var_rec[2] - Variant number
		CFG_DRV_ACT_RANGES_PRI_2_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_PRI_2_TOP			//		 - Top Compatibility
	 },
#else // HCF_PRI_VAR_2
	 { 0, 0, 0 },
#endif // HCF_PRI_VAR_2
#if defined HCF_PRI_VAR_3
	 {	3,									//var_rec[2] - Variant number
		CFG_DRV_ACT_RANGES_PRI_3_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_PRI_3_TOP			//		 - Top Compatibility
	 }
#else // HCF_PRI_VAR_3
	 { 0, 0, 0 }
#endif // HCF_PRI_VAR_3
	}
} ;


struct CFG_RANGE4_STRCT BASED cfg_drv_act_ranges_sta = {
	sizeof(cfg_drv_act_ranges_sta)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_STA,		// (0x0829)

	COMP_ROLE_ACT,
	COMP_ID_STA,
	{
#if defined HCF_STA_VAR_1
	 {	1,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_STA_1_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_STA_1_TOP			//		 - Top Compatibility
	 },
#else // HCF_STA_VAR_1
	 { 0, 0, 0 },
#endif // HCF_STA_VAR_1
#if defined HCF_STA_VAR_2
	 {	2,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_STA_2_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_STA_2_TOP			//		 - Top Compatibility
	 },
#else // HCF_STA_VAR_2
	 { 0, 0, 0 },
#endif // HCF_STA_VAR_2
// For Native_USB (Not used!)
#if defined HCF_STA_VAR_3
	 {	3,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_STA_3_BOTTOM,		//		 - Bottom Compatibility
		CFG_DRV_ACT_RANGES_STA_3_TOP			//		 - Top Compatibility
	 },
#else // HCF_STA_VAR_3
	 { 0, 0, 0 },
#endif // HCF_STA_VAR_3
// Warp
#if defined HCF_STA_VAR_4
	 {	4,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_STA_4_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_STA_4_TOP			//           - Top Compatibility
	 }
#else // HCF_STA_VAR_4
	 { 0, 0, 0 }
#endif // HCF_STA_VAR_4
	}
} ;


struct CFG_RANGE6_STRCT BASED cfg_drv_act_ranges_hsi = {
	sizeof(cfg_drv_act_ranges_hsi)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_HSI,		// (0x082A)
	COMP_ROLE_ACT,
	COMP_ID_HSI,
	{
#if defined HCF_HSI_VAR_0					// Controlled deployment
	 {	0,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_0_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_0_TOP			//           - Top Compatibility
	 },
#else // HCF_HSI_VAR_0
	 { 0, 0, 0 },
#endif // HCF_HSI_VAR_0
#if defined HCF_HSI_VAR_1					// WaveLAN
	 {	1,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_1_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_1_TOP			//           - Top Compatibility
	 },
#else // HCF_HSI_VAR_1
	 { 0, 0, 0 },
#endif // HCF_HSI_VAR_1
#if defined HCF_HSI_VAR_2					// Mini PCI
	 {	2,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_2_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_2_TOP			//           - Top Compatibility
	 },
#else // HCF_HSI_VAR_2
	 { 0, 0, 0 },
#endif // HCF_HSI_VAR_2
#if defined HCF_HSI_VAR_3					// Chico Lama
	 {	3,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_3_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_3_TOP			//           - Top Compatibility
	 },
#else // HCF_HSI_VAR_3
	 { 0, 0, 0 },
#endif // HCF_HSI_VAR_3
#if defined HCF_HSI_VAR_4					// Hermes-II all types
	 {	4,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_4_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_4_TOP			//           - Top Compatibility
	 },
#else // HCF_HSI_VAR_4
	 { 0, 0, 0 },
#endif // HCF_HSI_VAR_4
#if defined HCF_HSI_VAR_5					// WARP Hermes-2.5
	 {	5,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_HSI_5_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_HSI_5_TOP			//           - Top Compatibility
	 }
#else // HCF_HSI_VAR_4
	 { 0, 0, 0 }
#endif // HCF_HSI_VAR_4
	}
} ;

//--#endif // HCF_HII

CFG_RANGE4_STRCT BASED cfg_drv_act_ranges_apf = {
	sizeof(cfg_drv_act_ranges_apf)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_APF,		// (0x082B)

	COMP_ROLE_ACT,
	COMP_ID_APF,
	{
#if defined HCF_APF_VAR_1				//(Fake) Hermes-I
	 {	1,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_APF_1_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_APF_1_TOP			//           - Top Compatibility
	 },
#else // HCF_APF_VAR_1
	 { 0, 0, 0 },
#endif // HCF_APF_VAR_1
#if defined HCF_APF_VAR_2				//Hermes-II
	 {	2,									// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_APF_2_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_APF_2_TOP			//           - Top Compatibility
	 },
#else // HCF_APF_VAR_2
	 { 0, 0, 0 },
#endif // HCF_APF_VAR_2
#if defined HCF_APF_VAR_3						// Native_USB
	 {	3,										// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_APF_3_BOTTOM,		//           - Bottom Compatibility	!!!!!see note below!!!!!!!
		CFG_DRV_ACT_RANGES_APF_3_TOP			//           - Top Compatibility
	 },
#else // HCF_APF_VAR_3
	 { 0, 0, 0 },
#endif // HCF_APF_VAR_3
#if defined HCF_APF_VAR_4						// WARP Hermes 2.5
	 {	4,										// var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_APF_4_BOTTOM,		//           - Bottom Compatibility	!!!!!see note below!!!!!!!
		CFG_DRV_ACT_RANGES_APF_4_TOP			//           - Top Compatibility
	 }
#else // HCF_APF_VAR_4
	 { 0, 0, 0 }
#endif // HCF_APF_VAR_4
	}
} ;
#define HCF_VERSION  TEXT( "HCF$Revision: 1.368 $" )

static struct /*CFG_HCF_OPT_STRCT*/ {
	hcf_16	len;					//length of cfg_hcf_opt struct
	hcf_16	typ;					//type 0x082C
	hcf_16	v00;						//offset HCF_VERSION
	hcf_16	v01;						// HCF_ALIGN,
	hcf_16	v02;						// HCF_ASSERT,
	hcf_16	v03;						// HCF_ENCAPSULATION,
	hcf_16	v04;						// HCF_BIG_ENDIAN,
	hcf_16	v05;						// HCF_DLV | HCF_DLNV,
	hcf_16	v06;						// HCF_EX_INT,
	hcf_16	v07;						// HCF_INT_ON,
	hcf_16	v08;						// HCF_MAX_MSG,
	hcf_16	v09;						// HCF_MB_ON,
	hcf_16	v10;						// HCF_MEM_IO,
	hcf_16	v11;						// HCF_PROT_TIME,
	hcf_16	v12;						// HCF_TALLIES,
	hcf_16	v13;						// HCF_TALLIES_EXTRA,
	hcf_16	v14;						// HCF_TYPE,
	hcf_16	v15;						// HCF_MAX_LTV,
	hcf_16	v_min_2;	//v16			// MSF_COMPONENT_ID
	hcf_16	v_min_1;	//v17			// offset tallies,
	TCHAR	val[sizeof(HCF_VERSION)];
} BASED cfg_hcf_opt = {
	sizeof(cfg_hcf_opt)/sizeof(hcf_16) -1,
	CFG_HCF_OPT,				// (0x082C)
	( sizeof(cfg_hcf_opt) - sizeof(HCF_VERSION) - 4 )/sizeof(hcf_16),
	HCF_ALIGN,
	HCF_ASSERT,
	HCF_ENCAPSULATION,
	HCF_BIG_ENDIAN,
	HCF_DLV | HCF_DLNV,
	HCF_EX_INT,
	HCF_INT_ON,
	HCF_MAX_MSG,
	HCF_MB_ON,
	HCF_MEM_IO,
	HCF_PROT_TIME,
	HCF_TALLIES,
#if HCF_TALLIES_EXTRA
	HCF_TALLIES_EXTRA,
#else
	0,
#endif // HCF_TALLIES_EXTRA
	HCF_TYPE,
	HCF_MAX_LTV,
#if defined MSF_COMPONENT_ID
	MSF_COMPONENT_ID,
#else
	0,
#endif
#if HCF_TALLIES & 0x0003
	offsetof(IFB_STRCT, IFB_Tally ),
#else
	0,
#endif // HCF_TALLIES
	HCF_VERSION
}; // cfg_hcf_opt


#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == HCF_TYPE_USB
//						Actor USB Converter board I/F
CFG_RANGE2_STRCT BASED cfg_drv_act_ranges_ubi = {
	sizeof(cfg_drv_act_ranges_ubi)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_UBI,					// (0x0886)

	COMP_ROLE_ACT,
	COMP_ID_UBI,							// (0x19)
	{
	 {	1,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_UBI_1_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_UBI_1_TOP			//           - Top Compatibility
	 },
	 {	2,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_UBI_2_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_UBI_2_TOP			//           - Top Compatibility
	 }
	}
} ;


//						Actor USB Bootloader I/F
CFG_RANGES_STRCT BASED cfg_drv_act_ranges_udi = {
	sizeof(cfg_drv_act_ranges_udi)/sizeof(hcf_16) - 1,	//length of RID
	CFG_DRV_ACT_RANGES_UDI,					// (0x0885)

	COMP_ROLE_ACT,
	COMP_ID_UDI,							// (0x1A)
	{
	 {	1,									//var_rec[1] - Variant number
		CFG_DRV_ACT_RANGES_UDI_1_BOTTOM,		//           - Bottom Compatibility
		CFG_DRV_ACT_RANGES_UDI_1_TOP			//           - Top Compatibility
	 }
	}
} ;
#endif // HCF_TYPE_USB / HCF_TYPE_HII

#endif // MSF_COMPONENT_ID

#if defined MSF_COMPONENT_ID || HCF_MB_ON
#if HCF_MB_ON
HCF_STATIC LTV_STRCT BASED cfg_null = { 1, CFG_NULL };
#endif // HCF_MB_ON
HCF_STATIC hcf_16* BASED xxxx[ ] = {
#if HCF_MB_ON
	&cfg_null.len,							//CFG_NULL						0x0820
#endif // HCF_MB_ON
#if defined MSF_COMPONENT_ID
	&cfg_drv_identity.len,					//CFG_DRV_IDENTITY              0x0826
	&cfg_drv_sup_range.len,					//CFG_DRV_SUP_RANGE             0x0827
	&cfg_drv_act_ranges_pri.len,			//CFG_DRV_ACT_RANGES_PRI        0x0828
	&cfg_drv_act_ranges_sta.len,			//CFG_DRV_ACT_RANGES_STA		0x0829
	&cfg_drv_act_ranges_hsi.len,			//CFG_DRV_ACT_RANGES_HSI		0x082A
	&cfg_drv_act_ranges_apf.len,			//CFG_DRV_ACT_RANGES_APF		0x082B
	&cfg_hcf_opt.len,						//CFG_HCF_OPT					0x082C
#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == HCF_TYPE_USB
	&cfg_drv_act_ranges_udi.len,			//CFG_DRV_ACT_RANGES_UDI		0x0885
	&cfg_drv_act_ranges_ubi.len,			//CFG_DRV_ACT_RANGES_UBI		0x0886
#endif // HCF_TYPE_USB /HCF_TYPE_HII
#if (HCF_TYPE) & HCF_TYPE_HII
	NULL,									//IFB_PRIIdentity placeholder	0xFD02
	NULL,									//IFB_PRISup placeholder		0xFD03
#endif // HCF_TYPE_HII
#endif // MSF_COMPONENT_ID
	NULL									//endsentinel
  };
#define xxxx_PRI_IDENTITY_OFFSET	(sizeof(xxxx)/sizeof(xxxx[0]) - 3)

#endif // MSF_COMPONENT_ID / HCF_MB_ON


/************************************************************************************************************
************************** T O P   L E V E L   H C F   R O U T I N E S **************************************
************************************************************************************************************/

#if ! defined HCF_DL_ONLY
/***********************************************************************************************************/
/**
*
*.MODULE		hcf_action
*.DESCRIPTION	Changes the run-time Card behavior
*
*.ARGUMENTS
*  int hcf_action( IFBP ifbp, hcf_16 action )
*
*.RETURNS
*	HCF_SUCCESS				all (including invalid)
*	IFB_IntOffCnt			HCF_ACT_INT_FORCE_ON, HCF_ACT_INT_ON
*	HCF_INT_PENDING			HCF_ACT_INT_OFF, interrupt pending
*	HCF_ERR_NO_NIC			HCF_ACT_INT_OFF
*
*.NARRATIVE
*
* Parameters:
*  ifbp		address of the Interface Block
*  action	number identifying the type of change
*
*  o HCF_ACT_INT_FORCE_ON	enable interrupt generation by WaveLAN NIC
*  o HCF_ACT_INT_OFF		disable interrupt generation by WaveLAN NIC
*  o HCF_ACT_INT_ON			compensate 1 HCF_ACT_INT_OFF, enable interrupt generation if balance reached
*  o HCF_ACT_RX_ACK			Acknowledge receiver to Hermes
*  o HCF_ACT_TALLIES		Hermes Inquire Tallies (F100) command
*  o HCF_ACT_SCAN			Hermes Inquire Scan (F101) command
*  o HCF_ACT_ACS_SCAN		Hermes Automatic Channel Select Scan (F102) command
*  o HCF_ACT_WAKEUP			Disconnected Deep Sleep
*  o HCF_ACT_SLEEP			Disconnected Deep Sleep
*  o HCF_ACT_CCX_ON			Cisco CKIP feature
*  o HCF_ACT_CCX_OFF		Cisco CKIP feature
*
* Returns:
*  o HCF_ACT_INT_OFF
*		0: no interrupt pending
*		1: interrupt pending
*		   HCF_ERR_NO_NIC
*  o HCF_ACT_INT_FORCE_ON, HCF_ACT_INT_ON
*		as an aid during debugging, IFB_IntOffCnt is reported
*  o all other
*		0
*
* Remarks:
*	HCF_ACT_INT_OFF/HCF_ACT_INT_ON codes may be nested but must be balanced. The INT_OFF/INT_ON housekeeping
*	is initialized at 0x0000 by hcf_connect, causing the interrupt generation mechanism to be disabled at
*	first. This suits MSF implementation based on a polling strategy. An MSF based on a interrupt strategy
*	must call hcf_action with INT_ON in its initialization logic.
*
*.DIAGRAM
* 0: The assert embedded in HCFLOGENTRY checks against re-entrancy. Re-entrancy could be caused by a MSF logic
*	at task-level calling hcf_functions without shielding with HCF_ACT_ON/_OFF. However the HCF_ACT_INT_OFF
*	action itself can per definition not be protected this way. Based on code inspection, it can be concluded,
*	that there is no re-entrancy PROBLEM in this particular flow. It does not seem worth the trouble to
*	explicitly check for this condition (although there was a report of an MSF which ran into this assert.
* 2:IFB_IntOffCnt is used to balance the INT_OFF and INT_ON calls.  Disabling of the interrupts is achieved by
*	writing a zero to the Hermes IntEn register.  In a shared interrupt environment (e.g. the mini-PCI NDIS
*	driver) it is considered more correct to return the status HCF_INT_PENDING if and only if, the current
*	invocation of hcf_service_nic is (apparently) called in the ISR when the ISR was activated as result of a
*	change in HREG_EV_STAT matching a bit in HREG_INT_EN, i.e. not if invoked as result of another device
*	generating an interrupt on the shared interrupt line.
*	Note 1: it has been observed that under certain adverse conditions on certain platforms the writing of
*	HREG_INT_EN can apparently fail, therefor it is paramount that HREG_INT_EN is written again with 0 for
*	each and every call to HCF_ACT_INT_OFF.
*	Note 2: it has been observed that under certain H/W & S/W architectures this logic is called when there is
*	no NIC at all. To cater for this, the value of HREG_INT_EN is validated. If the unused bit 0x0100 is set,
*	it is assumed there is no NIC.
*	Note 3: During the download process, some versions of the F/W reset HREG_SW_0, hence checking this
*	register for HCF_MAGIC (the classical NIC presence test) when HCF_ACT_INT_OFF is called due to another
*	card interrupting via a shared IRQ during a download, fails.
*4:	The construction "if ( ifbp->IFB_IntOffCnt-- == 0 )" is optimal (in the sense of shortest/quickest
*	path in error free flows) but NOT fail safe in case of too many INT_ON invocations compared to INT_OFF).
*	When a real life MSF programmer ran into a MSF sequence problem, exactly causing that problem, he
*	was annoyed. As a side-effect of this unhappy MSF programmer adventures to find his problem, the
*	return status is defined to reflect the IFB_IntOffCnt.
*	Note that this is solely intended to aid debugging, no MSF logic should depend on this feature, no
*	guarantees for the future are given.
*	Enabling of the interrupts is achieved by writing the Hermes IntEn register.
*	 - If the HCF is in Defunct mode, the interrupts stay disabled.
*	 - Under "normal" conditions, the HCF is only interested in Info Events, Rx Events and Notify Events.
*	 - When the HCF is out of Tx/Notify resources, the HCF is also interested in Alloc Events.
*6:	ack the "old" Rx-event. See "Rx Buffer free strategy" in hcf_service_nic above for more explanation.
*	IFB_RxFID and IFB_RxLen must be cleared to bring both the internal HCF house keeping and the information
*	supplied to the MSF in the state "no frame received".
*8:	The HCF_ACT_SCAN, HCF_ACT_ACS_SCAN and HCF_ACT_TALLIES activity are merged by "clever" algebraic
*	manipulations of the RID-values and action codes, so foregoing robustness against migration problems for
*	ease of implementation. The assumptions about numerical relationships between CFG_TALLIES etc and
*	HCF_ACT_TALLIES etc are checked by the "#if" statements just prior to the body of this routine, resulting
*	in: err "maintenance" during compilation if the assumptions are no longer met. The writing of HREG_PARAM_1
*	with 0x3FFF in case of an ACS scan, is a kludge to get around lack of specification, hence different
*	implementation in F/W and Host.
*	When there is no NIC RAM available, some versions of the Hermes F/W do report 0x7F00 as error in the
*	Result field of the Status register and some F/W versions don't. To mask this difference to the MSF all
*	return codes of the Hermes are ignored ("best" and "most simple" solution to these types of analomies with
*	an acceptable loss due to ignoring all error situations as well).
*	The tallying of "No inquire space" is done by cmd_exe.
*30: do not HCFASSERT( rc, rc ) since rc == HCF_INT_PENDING is no error
*
*.ENDOC				END DOCUMENTATION
*
*************************************************************************************************************
*/
#if CFG_SCAN != CFG_TALLIES - HCF_ACT_TALLIES + HCF_ACT_SCAN
err "maintenance" apparently inviolated the underlying assumption about the numerical values of these macros
#endif
#if CFG_ACS_SCAN != CFG_TALLIES - HCF_ACT_TALLIES + HCF_ACT_ACS_SCAN
err "maintenance" apparently inviolated the underlying assumption about the numerical values of these macros
#endif
int
hcf_action( IFBP ifbp, hcf_16 action )
{
int	rc = HCF_SUCCESS;

	HCFLOGENTRY( HCF_TRACE_ACTION, action )														/* 0 */
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
#if HCF_INT_ON
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFF ||
			   action == HCF_ACT_INT_OFF || action == HCF_ACT_INT_FORCE_ON,  action ) ;
#endif // HCF_INT_ON

	switch (action) {
#if HCF_INT_ON
hcf_16	i;
	  case HCF_ACT_INT_OFF:						// Disable Interrupt generation
#if HCF_DEEPSLEEP || HCF_DDS
		if ( ifbp->IFB_IntOffCnt == 0xFFFE ) {	// DeepSleep WakeUp test
#if HCF_DDS
			rc = HCF_ERR_SLEEP;
			break;
#else // (HCF_DEEPSLEEP)
			ifbp->IFB_IntOffCnt++;						// restore conventional I/F
			OPW(HREG_CNTL, HREG_CNTL_WAKEUP_SYNC );		// set wakeup bit
			// 800 us latency before FW switches to high power
			MSF_WAIT(800);								// MSF-defined function to wait n microseconds.
#endif // HCF_DDS
		}
#endif // HCF_DEEPSLEEP, HCF_DDS
/*2*/	ifbp->IFB_IntOffCnt++;
//!     rc = 0;
		i = IPW( HREG_INT_EN );
		OPW( HREG_INT_EN, 0 );
		if ( i & 0x1000 ) rc = HCF_ERR_NO_NIC;
		else if ( i & IPW( HREG_EV_STAT ) ) rc = HCF_INT_PENDING;
		break;

	  case HCF_ACT_INT_FORCE_ON:				// Enforce Enable Interrupt generation
		ifbp->IFB_IntOffCnt = 0;
		//Fall through in HCF_ACT_INT_ON

	  case HCF_ACT_INT_ON:						// Enable Interrupt generation
/*4*/	if ( ( rc = ifbp->IFB_IntOffCnt-- ) == 0 && !(ifbp->IFB_CardStat & CARD_STAT_DEFUNCT) ) {
			OPW( HREG_INT_EN, ifbp->IFB_RscInd ?
							  HREG_EV_SLEEP_REQ | HREG_EV_INFO | HREG_EV_RX | HCF_EX_INT :
							  HREG_EV_SLEEP_REQ | HREG_EV_INFO | HREG_EV_RX | HCF_EX_INT | HREG_EV_ALLOC );
#if HCF_DEEPSLEEP || HCF_DDS
			if ( IPW(HREG_EV_STAT) & HREG_EV_SLEEP_REQ ) {
#if HCF_DDS
				OPW( HREG_INT_EN, 0 );	/*;?should be superfluous, if all parties involved (F/W, MSF and HCF)
										  * stick to the I/F, but it improveness robustness against
										  * I/F violations */
#endif // HCF_DDS
				// firmware indicates it would like to go into sleep modus
				// on ack, the firmware indeed goes into this modus
				// for now, I'll do a single ack...., with setting the 'DV' bit
				ifbp->IFB_IntOffCnt--;
				OPW( HREG_EV_ACK, HREG_EV_SLEEP_REQ | HREG_EV_ACK_REG_READY );
			}
#endif // HCF_DEEPSLEEP / HCF_DDS
		}
		break;
#endif // HCF_INT_ON

#if HCF_DDS
	  case HCF_ACT_SLEEP:						// DDS Sleep request
		hcf_cntl( ifbp, HCF_CNTL_DISABLE );
		cmd_exe( ifbp, HCMD_SLEEP, 0 );
		break;
	  case HCF_ACT_WAKEUP:						// DDS Wakeup request
		HCFASSERT( ifbp->IFB_IntOffCnt == 0xFFFE, ifbp->IFB_IntOffCnt )
		ifbp->IFB_IntOffCnt++;					// restore conventional I/F
		OPW( HREG_IO, HREG_IO_WAKEUP_ASYNC );
		MSF_WAIT(800);							// MSF-defined function to wait n microseconds.
		rc = hcf_action( ifbp, HCF_ACT_INT_OFF );	/*bogus, IFB_IntOffCnt == 0xFFFF, so if you carefully look
													 *at the #if HCF_DDS statements, HCF_ACT_INT_OFF is empty
													 *for DDS. "Much" better would be to merge the flows for
													 *DDS and DEEP_SLEEP, but since we are in "nearly release"
													 *mode, we don't touch the code
													 */
		break;
#endif // HCF_DDS

#if (HCF_TYPE) & HCF_TYPE_CCX
	  case HCF_ACT_CCX_ON:						// enable CKIP
	  case HCF_ACT_CCX_OFF:						// disable CKIP
		ifbp->IFB_CKIP_Enabled = action;
		break;
#endif // HCF_TYPE_CCX

#if ! ((HCF_TYPE) & HCF_TYPE_USB)
	  case HCF_ACT_RX_ACK:						//Receiver ACK
/*6*/	if ( ifbp->IFB_RxFID ) DAWA_ACK( HREG_EV_RX );
		ifbp->IFB_RxFID = ifbp->IFB_RxLen = 0;
		break;
#endif // HCF_TYPE_USB
		break;

/*8*/ case	HCF_ACT_ACS_SCAN:					// Hermes ACS Scan (F102)
		IF_NOT_USB( OPW( HREG_PARAM_1, 0x3FFF ); )
			//Fall through in HCF_ACT_TALLIES
	  case HCF_ACT_TALLIES:						// Hermes Inquire Tallies (F100)
	  case HCF_ACT_SCAN:						// Hermes Inquire Scan (F101)
		/*!! the assumptions about numerical relationships between CFG_TALLIES etc and HCF_ACT_TALLIES etc
		 *   are checked by #if statements just prior to this routine resulting in: err "maintenance"	*/
#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == ( HCF_TYPE_USB | HCF_TYPE_HII )
		{
			LTV_STRCT rid_fid = { 1/*len*/, 1/*type*/, {0}/* val[0]*/};
			//hcf_16 rid_fid[3]={1,1,0};	// get fid structure with safety word added to protect the stack
			rid_fid.typ = (hcf_16)(action - HCF_ACT_TALLIES + CFG_TALLIES);
			/*  Remark, only works in message mode */
			uhs_com_rid_ifs( ifbp->IFB_USBIOp, TRUE /*read*/, &rid_fid,	HCF_USB_RID_IFS_TIMEOUT_MSEC);
		}
#else
		cmd_exe( ifbp, HCMD_INQUIRE, action - HCF_ACT_TALLIES + CFG_TALLIES );
#endif // HCF_TYPE_USB / HCF_TYPE_HII
		break;

	  default:
		HCFASSERT( DO_ASSERT, action )
		break;
	}
	//! do not HCFASSERT( rc == HCF_SUCCESS, rc )														/* 30*/
	HCFLOGEXIT( HCF_TRACE_ACTION )
	return rc;
} // hcf_action
#endif // HCF_DL_ONLY


#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		hcf_cntl
*.DESCRIPTION	Enables or disables data transmission and reception on a specific port
*.ARGUMENTS
*	int hcf_cntl( IFBP ifbp, hcf_16 cmd )
*.RETURNS
*	HCF_SUCCESS
*	HCF_ERR_NO_NIC
*	>>cmd_wait
*
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	cmd			0x0001:	2 = disable, 1 = enable
*				0x0700: port to be enabled (range HCF_PORT_0 through HCF_PORT_6)
*
*  Description:
*
*.DIAGRAM
*	hcf_cntl takes successively the following actions:
*2:	If the HCF is incompatible with the Primary or Station Supplier in the Hermes, hcf_cntl() returns
*	immediately with HCF_ERR_NO_NIC as status.
*4:	Masking out all non-port bits prevents screwing up the Hermes via cmd_exe
*6:	To assure that all static configurations are activated, the Hermes enable command is preceded by
*	an Hermes disable command.
*
*.NOTICE
*	When the Hermes enable cmd is given, the static configuration of the Hermes is done.  In case of the first
*	port to be enabled, this is the NIC wide as well as the particular port configuration. If already one or
*	more ports are enabled, only the static configuration of that particular port is done.
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
#if HCMD_ENABLE != 0x0001 || HCMD_ENABLE != HCF_CNTL_ENABLE || HCMD_ENABLE & (HCF_CNTL_DISABLE|HFS_TX_CNTL_PORT)
potentially disaster, the coding is inherently based on HCMD_ENABLE being a single bit,
fully disjunct with HCMD_DISABLE and HFS_TX_CNTL_PORT, and the same numerical value as HCMD_ENABLE
#endif // HCMD_ENABLE
#if HCMD_DISABLE != 0x0002 || HCMD_DISABLE != HCF_CNTL_DISABLE || HCMD_DISABLE & (HCMD_ENABLE|HFS_TX_CNTL_PORT)
potentially disaster, the coding is inherently based on HCMD_DISABLE being a single bit,
fully disjunct with HCMD_ENABLE and HFS_TX_CNTL_PORT, not necissarily the same numerical value as HCF_CNTL_DISABLE
#endif // HCMD_ENABLE


int
hcf_cntl( IFBP ifbp, hcf_16 cmd )
{

int	rc = HCF_ERR_NO_NIC;	/*;?this preset is not really needed in case the NIC is really absent
							  because cmd_exe will fail. In case of CARD_STAT_INCOMP_... it may not be the
							  most optimal value */

	HCFASSERT( (cmd & HCF_CNTL_ENABLE) ^ (cmd & HCF_CNTL_DISABLE), cmd )
	HCFASSERT( (cmd & ~(HCMD_ENABLE|HCMD_DISABLE|HFS_TX_CNTL_PORT) ) == 0, cmd ) //;!  HFS_TX_CNTL_PORT is 0 for pure Station
	if ( ( ifbp->IFB_CardStat & ( CARD_STAT_INCOMP_PRI|CARD_STAT_INCOMP_STA ) ) == 0 ) {				 /*2*/
/*4*/	cmd &= HFS_TX_CNTL_PORT | HCMD_ENABLE;
/*6*/	rc = cmd_exe( ifbp, (hcf_16)(HCMD_DISABLE | (cmd & HFS_TX_CNTL_PORT) ), 0 );
/* ;?!!!!! moved from other places , but this place is plain wrong. It must be somewhere in the initialization
   ;?!!!!! flow. This place will do to get going for the time being with simple driver
*/
		if ( rc == HCF_SUCCESS && cmd & HCMD_ENABLE ) {
			rc = cmd_exe( ifbp, (hcf_16)( cmd & ( HFS_TX_CNTL_PORT | HCMD_ENABLE ) ), 0 );
#ifdef HCF_DMA
			//!rlav: has to do with reclaiming of packets in function 'hcf_get_tx_packets'
			ifbp->dma_port_is_enabled = 1;
			// make the entire rx descriptor chain DMA-owned, so the DMA engine can (re-)use it.
			p = ifbp->dma_rx_first_desc;
			while ( p ) {
				p->buf_cntl.cntl_stat |= DESC_CNT_CTRL_DMAOWNED;
				p = p->next_desc_addr;
		}
			if ( ifbp->dma_rx_first_desc ) {
				// a rx chain is available so hand it over to the DMA engine
				OUT_PORT_DWORD( io_port + HREG_RXDMA_PTR32, ifbp->dma_rx_first_desc->desc_phys_addr);
		}
#endif // HCF_DMA
		}
#ifdef HCF_DMA
		if (!(cmd & HCMD_ENABLE))
		{
			p = ifbp->dma_tx_first_desc;
			// when the port is disabled, the DMA engine needs to be de-activated, so the host can
			// safely reclaim tx packets from the tx descriptor chain.

			OUT_PORT_DWORD( io_port + HREG_DMA_CTRL, DMA_CTRLSTAT_RESET);

			OUT_PORT_DWORD( io_port + HREG_DMA_CTRL, DMA_CTRLSTAT_GO);
			// make the entire tx descriptor chain host-owned, so the host can reclaim it.
			while(p)
			{
				p->buf_cntl.cntl_stat &= ~DESC_CNT_CTRL_DMAOWNED;
				p = p->next_desc_addr;
			}
			// make the entire rx descriptor chain host-owned, so the MSF can reclaim it.
			p = ifbp->dma_rx_first_desc;
			while(p)
			{
				SET_BUFCOUNT(p,0u); // make clear to the MSF that rx chain contains non-valid data
				p->buf_cntl.cntl_stat &= ~DESC_CNT_CTRL_DMAOWNED;
				p = p->next_desc_addr;
			}
			ifbp->dma_port_is_enabled = 0; //!rlav note: port gets also disabled after reset or init command!
		}
#endif
	}
	HCFASSERT(rc == HCF_SUCCESS, rc )
	return rc;
} // hcf_cntl
#endif // HCF_DL_ONLY


/************************************************************************************************************
*
*.MODULE		hcf_connect
*.DESCRIPTION	Initializes Card and HCF housekeeping
*
*.ARGUMENTS
*	void hcf_connect( IFBP ifbp, hcf_io io_base )
*
*.RETURNS
*	HCF_SUCCESS
*	HCF_ERR_DEFUNCT_CMD_SEQ
*	HCF_ERR_NO_NIC
*	HCF_ERR_TIME_OUT
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	io_base		USB:	 HCF_CONNECT, HCF_DISCONNECT
*				non-USB: I/O Base address of the NIC (connect), HCF_DISCONNECT
*
*	MSF-accessible fields of Result Block:
*	IFB_IOBase				entry parameter io_base
*	IFB_IORange				HREG_IO_RANGE (0x40)
*	IFB_Version				version of the IFB layout
*
*.DIAGRAM
*0:	Throughout hcf_connect you need to distinguish the connect from the disconnect case, which requires
*	some attention about what to use as "I/O" address when for which purpose.
*2:
*2a: Reset H-II by toggling reset bit in IO-register on and off.
*	The HCF_TYPE_PRELOADED caters for the DOS environment where H-II is loaded by a seperate program to
*	overcome the 64k size limit posed on DOS drivers.
*	The macro OPW is not yet useable because the IFB_IOBase field is not set.
*	Note: hopefully merging this rest with the clearing and initializing of the IFB (see below, item 4) acts
*	as a delay which meets the specification for S/W reset.
*	As an aditional problem, on Hermes 2.5.1 we must make sure the SRESET port is only written  2 time.
*	On Hermes 2.5.1, writing the reset continuouly ( to waste some additional time) will stall the Hermes and
*	freeze Windows
*	This is a Hardware BUG
*2b: Experimentally it is determined over a wide range of F/W versions that waiting for the for Cmd bit in
*	Ev register gives a workable strategy. The available documentation does not give much clues.
*4:	clear and initialize the IFB
*	The HCF house keeping info is designed such that zero is the appropriate initial value for as much as
*	feasible IFB-items.
*	The readable fields mentioned in the description section and some HCF specific fields are given their
*	actual value.
*	IFB_TickIni is initialized at best guess before calibration
*	Hcf_connect defaults to "no interrupt generation" (implicitly achieved by the zero-filling).
*6:	Register compile-time linked MSF Routine and set default filter level
*	cast needed to get around the "near" problem in DOS COM model
*	er C2446: no conversion from void (__near __cdecl *)(unsigned char __far *,unsigned int,unsigned short,int)
*							to   void (__far  __cdecl *)(unsigned char __far *,unsigned int,unsigned short,int)
*8:	If a command is apparently still active (as indicated by the Busy bit in Cmd register) this may indicate a
*	blocked cmd pipe line.  To unblock the following actions are done:
*	 - Ack everything
*	 - Wait for Busy bit drop  in Cmd register
*	 - Wait for Cmd  bit raise in Ev  register
*	The two waits are combined in a single HCF_WAIT_WHILE to optimize memory size. If either of these waits
*	fail (prot_cnt becomes 0), then something is serious wrong. Rather than PANICK, the assumption is that the
*	next cmd_exe will fail, causing the HCF to go into DEFUNCT mode
*10:	Ack everything to unblock a (possibly blocked) cmd pipe line
*	Note 1: it is very likely that an Alloc event is pending and very well possible that a (Send) Cmd event is
*	pending on non-initial calls
*	Note 2: it is assumed that this strategy takes away the need to ack every conceivable event after an
*	Hermes Initialize
*12:	Only H-II NEEDS the Hermes Initialize command. Due to the different semantics for H-I and H-II
*	Initialize command, init() does not (and can not, since it is called e.g. after a download) execute the
*	Hermes Initialize command. Executing the Hermes Initialize command for H-I would not harm but not do
*	anything useful either, so it is skipped.
*	The return status of cmd_exe is ignored. It is assumed that if cmd_exe fails, init fails too
*14:	use io_base as a flag to merge hcf_connect and hcf_disconnect into 1 routine
*	the call to init and its subsequent call of cmd_exe will return HCF_ERR_NO_NIC if appropriate. This status
*	is (badly) needed by some legacy combination of NT4 and card services which do not yield an I/O address in
*	time.
*
*.NOTICE
*	On platforms where the NULL-pointer is not a bit-pattern of all zeros, the zero-filling of the IFB results
*	in an incorrect initialization of pointers.
*	The implementation of the MailBox manipulation in put_mb_info protects against the absence of a MailBox
*	based on IFB_MBSize, IFB_MBWp and ifbp->IFB_MBRp. This has ramifications on the initialization of the
*	MailBox via hcf_put_info with the CFG_REG_MB type, but it prevents dependency on the "NULL-"ness of
*	IFB_MBp.
*
*.NOTICE
*	There are a number of problems when asserting and logging hcf_connect, e.g.
*	 - Asserting on re-entrancy of hcf_connect by means of
*	 "HCFASSERT( (ifbp->IFB_AssertTrace & HCF_ASSERT_CONNECT) == 0, 0 )" is not useful because IFB contents
*	 are undefined
*	 - Asserting before the IFB is cleared will cause mdd_assert() to interpret the garbage in IFB_AssertRtn
*	 as a routine address
*	Therefore HCFTRACE nor HCFLOGENTRY is called by hcf_connect.
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
hcf_connect( IFBP ifbp, hcf_io io_base )
{
int			rc = HCF_SUCCESS;
hcf_io		io_addr;
hcf_32		prot_cnt;
hcf_8		*q;

#if ! ((HCF_TYPE) & HCF_TYPE_USB )
	if ( io_base != HCF_DISCONNECT ) io_addr = io_base;		//connect								/* 0 */
	else {													//disconnect
		io_addr = ifbp->IFB_IOBase;
		OPW( HREG_INT_EN, 0 );		//;?workaround against dying F/W on subsequent hcf_connect calls
	}
#endif // HCF_TYPE_USB

#if HCF_DEEPSLEEP
	OUT_PORT_WORD( io_addr + HREG_CNTL, HREG_CNTL_WAKEUP_SYNC );	// set wakeup bit, OPW not yet useable
	// 800 us latency before FW switches to high power
	MSF_WAIT(800);									// MSF-defined function to wait n microseconds.
#endif // HCF_DEEPSLEEP
	
#if ( (HCF_TYPE) & (HCF_TYPE_HII | HCF_TYPE_USB | HCF_TYPE_PRELOADED) ) == HCF_TYPE_HII
	OUT_PORT_WORD( io_addr + HREG_IO, HREG_IO_SRESET );	//OPW not yet useable						/* 2a*/
#endif // HCF_TYPE_HII / HCF_TYPE_USB /HCF_TYPE_PRELOADED
	for ( q = (hcf_8*)(&ifbp->IFB_MSFSup); q > (hcf_8*)ifbp; *--q = 0 ) /*NOP*/;					/* 4 */
#if defined MSF_COMPONENT_ID && (HCF_TYPE) & /*;?*/HCF_TYPE_HII //a new IFB demonstrates how dirty the solution is
	xxxx[xxxx_PRI_IDENTITY_OFFSET] = NULL;		//IFB_PRIIdentity placeholder	0xFD02
	xxxx[xxxx_PRI_IDENTITY_OFFSET+1] = NULL;	//IFB_PRISup placeholder		0xFD03
#endif // MSF_COMPONENT_ID / HCF_TYPE_HII
#if HCF_TALLIES
	ifbp->IFB_Tally = 1 + 2 * HCF_TOT_TAL_CNT;	//convert # of Tallies to L value for LTV
	ifbp->IFB_TallyTyp = CFG_TALLIES;			//IFB_TallyTyp: set T value
#endif // HCF_TALLIES
#if !( (HCF_TYPE) & HCF_TYPE_USB )
	ifbp->IFB_IOBase	= io_addr;					//set IO_Base asap, so asserts via HREG_SW_2 don't harm
	ifbp->IFB_IORange	= HREG_IO_RANGE;
#endif // HCF_TYPE_USB
#if HCF_ASSERT
	assert_ifbp = ifbp;							//just for the side effect of initializing assert_ifbp
	ifbp->IFB_AssertLvl = 1;					//just for the side effect of initializing IFB_AssertLvl
#if HCF_ASSERT & 0x8000
	if ( io_base != HCF_DISCONNECT ) ifbp->IFB_AssertRtn = (MSF_ASSERT_RTNP)msf_assert;	/* 6 */
#endif // HCF_ASSERT & 0x8000
	HCFASSERT( DO_ASSERT, 0xCAF0)				//just to proof that the complete assert machinery is working
	HCFASSERT( ((long)(void*)ifbp & (HCF_ALIGN-1) ) == 0, (long)(void*)ifbp )
#endif // HCF_ASSERT
	ifbp->IFB_Magic		= HCF_MAGIC;
	ifbp->IFB_Version	= IFB_VERSION;
	IF_PROT_TIME( prot_cnt = ifbp->IFB_TickIni = INI_TICK_INI; )
#if ! ( (HCF_TYPE) & HCF_TYPE_USB )
	HCFASSERT( (io_addr & 0x003F) == 0, io_addr )
#if ( (HCF_TYPE) & (HCF_TYPE_HII | HCF_TYPE_PRELOADED) ) == HCF_TYPE_HII
	OPW( HREG_IO, 0x0000 );						//OPW useable										/* 2b*/
	HCF_WAIT_WHILE( (IPW( HREG_EV_STAT) & HREG_EV_CMD) == 0 );
	IF_PROT_TIME( HCFASSERT( prot_cnt, IPW( HREG_EV_STAT) ) )
	IF_PROT_TIME( if ( prot_cnt ) prot_cnt = ifbp->IFB_TickIni; )
#endif // HCF_TYPE_HII / HCF_TYPE_USB /HCF_TYPE_PRELOADED
												//if Busy bit in Cmd register
	if (IPW( HREG_CMD ) & HCMD_BUSY ) {																/* 8 */
												//.  Ack all to unblock a (possibly) blocked cmd pipe line
		OPW( HREG_EV_ACK, (hcf_16)~HREG_EV_SLEEP_REQ );
												//.  Wait for Busy bit drop  in Cmd register
												//.  Wait for Cmd  bit raise in Ev  register
		HCF_WAIT_WHILE( ( IPW( HREG_CMD ) & HCMD_BUSY ) && (IPW( HREG_EV_STAT) & HREG_EV_CMD) == 0 );
		IF_PROT_TIME( HCFASSERT( prot_cnt, IPW( HREG_EV_STAT) ) ) /* if prot_cnt == 0, cmd_exe will fail, causing DEFUNCT */
	}
	OPW( HREG_EV_ACK, (hcf_16)~HREG_EV_SLEEP_REQ );
#endif // HCF_TYPE_USB
#if ( (HCF_TYPE) & (HCF_TYPE_HII | HCF_TYPE_PRELOADED) ) == HCF_TYPE_HII								/*12*/
#if  ((HCF_TYPE) & HCF_TYPE_USB)
	rc = uhs_com_action( ifbp->IFB_USBIOp,
						 UHS_COM_ACTION_TO_ADMA  ,
						 0, HCF_USB_ACTION_TIMEOUT_MSEC ); // back to initial mode when needed
#else
	(void)cmd_exe( ifbp, HCMD_INI, 0 );
#endif // HCF_TYPE_USB
#endif // HCF_TYPE_HII /HCF_TYPE_PRELOADED
	if ( io_base != HCF_DISCONNECT ) {
		rc = init( ifbp );													/*14*/
	} else HCFASSERT(  ( ifbp->IFB_Magic ^= HCF_MAGIC ) == 0, ifbp->IFB_Magic ) /*NOP*/;
	/* of above HCFASSERT only the side effect is needed, NOP in case HCFASSERT is dummy */
#if ! ( (HCF_TYPE) & HCF_TYPE_USB )
	ifbp->IFB_IOBase = io_base;																			/* 0*/
#endif
	return rc;
} // hcf_connect


#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		hcf_encap
*.DESCRIPTION	test whether len/type is E-II and if so, whether RFC1042 or Bridge-Tunnel
*				encapsulation is to be used.
*				The return value is ENC_NONE or the appropriate type
*
*.ARGUMENTS
*  hcf_8 hcf_encap( wci_bufp type )
*
*.RETURNS
*	ENC_NONE		len/type is "len" ( (BIG_ENDIAN)type <= 1500 )
*	ENC_TUNNEL		len/type is "type" and 0x80F3 or 0x8137
*	ENC_1042		len/type is "type" but not 0x80F3 or 0x8137
*
*.NARRATIVE
*
* Parameters:
*	type	len/type (in "network" Endian format)
*
*  1:	presume 802.3, hence preset return value at ENC_NONE
*  2:	convert type from "network" Endian format to native Endian
*  4:	the litmus test to distinguish type and len.
*	The hard code "magic" value of 1500 is intentional and should NOT be replaced by a mnemonic and definitely
*	NOT be HCF_MAX_MSG because it is not related at all to the maximum frame size supported  by the Hermes.
* 6:	check type against:
*		0x80F3	//AppleTalk Address Resolution Protocol (AARP)
*		0x8137	//IPX
*	to determine the type of encapsulation
*
************************************************************************************************************/
#if HCF_ENCAPSULATION	//i.e 0x0001 or 0x0002
hcf_8
hcf_encap( wci_bufp type )
{

hcf_8	rc = ENC_NONE;																					/* 1 */
hcf_16	t = (hcf_16)(*type<<8) + *(type+1);																/* 2 */

	if ( t > 1500 ) {																					/* 4 */
		if ( t == 0x8137 || t == 0x80F3 ) rc = ENC_TUNNEL;												/* 6 */
		else rc = ENC_1042;
	}
	return rc;
} // hcf_encap
#endif // HCF_ENCAPSULATION
#endif // HCF_DL_ONLY


/************************************************************************************************************
*
*.MODULE		hcf_get_info
*.DESCRIPTION	Obtains operational information as well as transient and persistent configuration information
*				from the Card and from the HCF
*
*.ARGUMENTS
*	int hcf_get_info( IFBP ifbp, LTVP ltvp )
*	Card Interrupts disabled
*
*.RETURNS
*	HCF_ERR_LEN
*	HCF_SUCCESS
*	HCF_ERR_NO_NIC		all cases except the "HCF embedded" pseudo RIDs
*	>>strio				>= CFG_RID_FW_MIN
*
*.NARRATIVE
*
*	parameters:
*		ifbp	address of the Interface Block
*		ltvp	address of LengthTypeValue structure specifying the "what" and the "how much" of the
*				information to be collected from the HCF or from the Hermes
*
*.NOTICE
*
*	"HCF embedded" pseudo RIDs:
*	CFG_MB_INFO, CFG_TALLIES, CFG_IFB, CFG_DRV_IDENTITY, CFG_DRV_SUP_RANGE, CFG_DRV_ACT_RANGES_PRI,
*	CFG_DRV_ACT_RANGES_STA, CFG_DRV_ACT_RANGES_HSI
*	Note the HCF_ERR_LEN is NOT adequately set, when L >= 2 but less than needed
*
*
*	Remarks: Transfers operation information and transient and persistent configuration information from the
*	Card and from the HCF to the MSF.
*	The exact layout of the provided data structure depends on the action code. Copying stops if either the
*	complete information is copied or if the number of words indicated by len is copied.  Len acts as a safe
*	guard against Configuration Information blocks which have different sizes for different Hermes versions,
*	e.g. when later versions support more tallies than earlier versions. It is a conscious decision that
*	unused parts of the PC RAM buffer are not cleared.
*
*	Remarks: hcf_get_info is protected against a "Read error" as result of Card removal.
*
*	The mechanism to HCF_ASSERT on invalid typ-codes in the LTV record is based on the following strategy:
*	  -	some codes (e.g. CFG_TALLIES) are explicitly handled by the HCF which implies that these codes
*		are valid
*	  - all other codes in the range 0xFC00 through 0xFFFF are passed to the Hermes.  The Hermes returns an
*		LTV record with a zero value in the L-field for all Typ-codes it does not recognize. This is
*		defined and intended behavior, so HCF_ASSERT does not catch on this phenomena.
*	  -	all remaining codes are invalid and cause an ASSERT.
*
*.DIAGRAM
*3:	tallying of "No inquire space" is done by cmd_wait
*
*	CFG_MB_INFO: copy the oldest MailBox Info Block or the "null" block if none available. The "null" block is
*	retrieved via lookup in the xxxx table . If len is less than the size of the MailBox Info block, only as
*	much as fits in the PC RAM buffer is copied.  The MailBox Read pointer is updated to point to the next
*	MailBox Info block, hence the remainder of an "oversized" MailBox Info block is lost.
*
*20: "HCFASSERT( rc == HCF_SUCCESS, rc )" is not suitable because this will always trigger as side effect of
*	the HCFASSERT in hcf_put_info which calls hcf_get_info to figure out whether the RID exists at all.
*
************************************************************************************************************/
int
hcf_get_info( IFBP ifbp, LTVP ltvp )
{

int				rc = HCF_SUCCESS;
hcf_16			len = ltvp->len;
hcf_16			type = ltvp->typ;
wci_bufp		cp = (wci_bufp)ltvp->val;		//destination char pointer (in LTV record)
												//;? once string_io is removed, the hcf_8/16* discrepancy can be resolved too
wci_recordp		p = &ltvp->len; //->val[0];					//destination word pointer (in LTV record)
hcf_16 /*FAR*/	*q = NULL;						//source word pointer  ;?DOS COM can't cope with FAR,
//FAR is only relevant for DOS, as a consequence MailBox must be near which is usually true anyway
hcf_io			io_port;
int				i;

	HCFLOGENTRY( HCF_TRACE_GET_INFO, ltvp->typ )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT
#if HCF_DDS
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFE, ltvp->typ )
#endif // HCF_DDS
	HCFASSERT( ltvp, 0 )
	HCFASSERT( 1 < ltvp->len && ltvp->len <= HCF_MAX_LTV, ltvp->len )
	HCFASSERT( 1 < ltvp->len && ltvp->len <= HCF_MAX_LTV, ltvp->typ )

	ltvp->len = 0;								//default to: No Info Available
#if defined MSF_COMPONENT_ID || HCF_MB_ON		//filter out all specials
	for ( i = 0; ( q = xxxx[i] ) != NULL && q[1] != type; i++ );
#endif // MSF_COMPONENT_ID
#if HCF_TALLIES
	if ( type == CFG_TALLIES ) {													/*3*/
		(void)hcf_action( ifbp, HCF_ACT_TALLIES );
		q = (hcf_16*)&ifbp->IFB_Tally;
	}
#endif // HCF_TALLIES
#if HCF_MB_ON
	if ( type == CFG_MB_INFO ) {
		if ( ifbp->IFB_MBInfoLen ) {
			if ( ifbp->IFB_MBp[ifbp->IFB_MBRp] == 0xFFFF ) ifbp->IFB_MBRp = 0; //;?Probably superfluous
			q = &ifbp->IFB_MBp[ifbp->IFB_MBRp];	//;? demand MailBox is near, impact on DOS-like environments
			ifbp->IFB_MBRp += *q + 1;	//update read pointer
			if ( ifbp->IFB_MBp[ifbp->IFB_MBRp] == 0xFFFF ) ifbp->IFB_MBRp = 0;
			ifbp->IFB_MBInfoLen = ifbp->IFB_MBp[ifbp->IFB_MBRp];
		}
	}
#endif // HCF_MB_ON
	if ( q != NULL ) {						//a special or CFG_TALLIES or CFG_MB_INFO
		i = min( len, *q ) + 1;				//total size of destination (including T-field)
		while ( i-- ) {
			*p++ = *q;
#if HCF_TALLIES & 0x8000
			if ( q > &ifbp->IFB_TallyTyp && type == CFG_TALLIES ) *q = 0;
#endif // HCF_TALLIES
			q++;
		}
	} else {								// not a special nor CFG_TALLIES nor CFG_MB_INFO
#if (HCF_TYPE) & HCF_TYPE_USB
		ltvp->len = len; // RESTORE maximum ltv length
#if (HCF_TYPE) & HCF_TYPE_HII
		rc =  uhs_com_rid_ifs( ifbp->IFB_USBIOp, TRUE, ltvp, HCF_USB_RID_IFS_TIMEOUT_MSEC);
#else
		get_info_usb( ifbp, ltvp );
#endif // HCF_TYPE_HII (within USB)
#else
		if ( type == CFG_PROD_DATA ) {  //only needed for a) H-I    b) some test tool on top of H-II NDIS driver
			HI_AUX_CNTL( ifbp, HREG_CNTL_AUX_ENA_CNTL );	//ignore return status, PDA check will fail supposedly
			OPW( HREG_AUX_PAGE, (hcf_16)(PLUG_DATA_OFFSET >> 7) );
			OPW( HREG_AUX_OFFSET, (hcf_16)(PLUG_DATA_OFFSET & 0x7E) );
			io_port = ifbp->IFB_IOBase + HREG_AUX_DATA;		//to prevent side effects of the MSF-defined macro
			cp = (wci_bufp)ltvp->val;		//destination char pointer (in LTV record)
			if ( ( i = len - 1 ) > 0 ) IN_PORT_STRING_16( io_port, cp, i );	//decrement compensates for space used by T
			HI_AUX_CNTL( ifbp, HREG_CNTL_AUX_DIS_CNTL );	//ignore return status, previous call apparently succeeded
		} else if ( type == CFG_CMD_HCF ) {
#define P ((CFG_CMD_HCF_STRCT FAR *)ltvp)
			HCFASSERT( P->cmd == 0x0000, P->cmd )							//only Rx-monitor supported
			if ( P->cmd == CFG_CMD_HCF_REG_ACCESS ) {
				HCFASSERT( P->mode <= HREG_AUX_DATA, P->mode )				//Reg space: 0x00 - 0x3F
				ltvp->len = min( len, 4 );									// RESTORE ltv length
				P->add_info = IPW( P->mode );
			}
#undef P
		} else if ( type >= CFG_RID_FW_MIN ) {
//;? by using HCMD_BUSY option when calling cmd_exe, using a strio with length 0 just to set up the
//;? BAP and calling cmd_cmpl, you could merge the 2 Busy waits. Whether this really helps (and what
//;? would be the optimal sequence in cmd_exe and strio) would have to be MEASURED
			if ( ( rc = cmd_exe( ifbp, HCMD_ACCESS, type ) ) == HCF_SUCCESS ) {
				rc = strio_16( ifbp, IO_IN, type, 0, (wci_recordp)ltvp, len+1 BE_PAR(2) );
			}
		} else HCFASSERT( DO_ASSERT, type ) /*NOP*/; //NOP in case HCFASSERT is dummy
#endif // HCF_TYPE_USB
	}
	if ( len < ltvp->len ) {
		ltvp->len = len;
		if ( rc == HCF_SUCCESS ) rc = HCF_ERR_LEN;
	}

	HCFASSERT( rc == HCF_SUCCESS ||
/*20*/		   ( rc == HCF_ERR_LEN && ifbp->IFB_AssertTrace & 1<<HCF_TRACE_PUT_INFO ), rc )
	HCFASSERT( rc == HCF_SUCCESS ||
			   ( rc == HCF_ERR_LEN && ifbp->IFB_AssertTrace & 1<<HCF_TRACE_PUT_INFO ), type )
	HCFLOGEXIT( HCF_TRACE_GET_INFO )
	return rc;

} // hcf_get_info


#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII) ) == HCF_TYPE_USB
int
get_info_usb( IFBP ifbp, LTVP ltvp )
{

int				rc = HCF_SUCCESS;
wci_recordp		p = ltvp->val;				//destination word pointer (in LTV record)
hcf_16			type = ltvp->typ;
hcf_16			len;
hcf_16			i;
hcf_16			*q;							//source pointer

	if ( ltvp->len > HCF_MAX_MSG ) ltvp->len = HCF_MAX_MSG; //;? just as a try to get around PDA problems
	len = ltvp->len;
	switch ( type ) {

// #if defined MSF_COMPONENT_ID
//	  case CFG_DRV_ACT_RANGES_UDI:			// (0x0885)
//	  case CFG_DRV_ACT_RANGES_UBI:			// (0x0886)
//		q = (hcf_16*)xuuu[ type - CFG_DRV_ACT_RANGES_UDI ];
//		ltvp->len = *q;
//		q += 2;								//skip L and T
//		if ( ltvp->len > len ) {
//			rc = HCF_ERR_LEN;
//			ltvp->len = len;
//		}
//		len = ltvp->len;
//		while ( --len ) *p++ = *q++;
//		break;
// #endif // MSF_COMPONENT_ID

#if HCF_DLV
	  case 0x880:		//;?create mnemonics
	  case CFG_UBI_SUP_RANGE:				//0x0881		//USB Converter board
	  case 0x882:		//;?create mnemonics
	  case CFG_UDI_SUP_RANGE:				//0x0883		//USB Bootloader
	  case 0x884:		//;?create mnemonics
					//pointer to length in bytes of (CFG_USB_MNGMT + overhead)
		p = (hcf_16*)(UNUGetUsbInfo(  ) )->ucLucentVersion;
		i = 2;		//compensate for overhead in Configuration Management Descriptor (CFG_USB_MNGMT)
					//convert word-offset to byte-offset and compensate length of CFG_USB_MNGMT for overhead
		while ( 2*i < (hcf_16)(*p-4) ) {
			if ( type == p[i] ) {	//match found, so copy this sub-LTV
				NdisMoveMemory( &ltvp->len, &p[i-1], (p[i-1]+1 ) * 2);
			}
			i += p[i-1]+1;		//add L of current sub-LTV + 1 (to reflect T)
		}
		break;

	  case CFG_PROD_DATA:
		HCFASSERT( len > 4, len )
		p/*ltvp->val*/[0] = CFG_MEM_READ;		//action
		p/*ltvp->val*/[1] = len - 1;				//len in words, including T  ;?hopefully this suffice
		p/*ltvp->val*/[2] = 0;					//offset
		rc = UNUSendHcfReq( type, &ltvp->val[ 0], 10, ltvp);
		break;
#endif // HCF_DLV

	  default:											//all "unknown" ones are passed to Hermes
		rc = UNUSendHcfReq( type, NULL, 0, ltvp);
	}
	return rc;
} // get_info_usb
#endif // HCF_TYPE_USB / HCF_TYPE_HII


/************************************************************************************************************
*
*.MODULE		hcf_put_info
*.DESCRIPTION	Transfers operation information and transient and persistent configuration information to the
* Card.
*
*.ARGUMENTS
*	int hcf_put_info( IFBP ifbp, LTVP ltvp )
*	Card Interrupts disabled
*
*.RETURNS
*	HCF_SUCCESS
*	>>cmd_exe
*	>>download				type == CFG_PROG
*	>>put_info				CFG_RID_CFG_MIN <= type <= CFG_RID_CFG_MAX
*	>strio_16
*
*.NARRATIVE
*
*	parameters:
*		ifbp		address of the Interface Block
*		ltvp		specifies the RID (as defined by Hermes I/F) or pseudo-RID (as defined by WCI)
*
*.NOTICE
*  Remarks:
*	The Hermes puts additional sequence constraints on the usage of hcf_put_info, e.g. CFG_NOTIFY is only
*	allowed after hcf_enable
*
*	Remarks:  In case of HCF Configuration LTVs, the type values are chosen in a range which does not overlap
*	the RID-range.
*
*.DIAGRAM
*
************************************************************************************************************/

int
hcf_put_info( IFBP ifbp, LTVP ltvp )
{

int rc = HCF_SUCCESS;

	HCFLOGENTRY( HCF_TRACE_PUT_INFO, ltvp->typ )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT
#if HCF_DDS
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFE, ltvp->typ )
#endif // HCF_DDS
	HCFASSERT( ltvp, 0 )
	HCFASSERT( ltvp->len && ltvp->len - 1 < HCF_MAX_LTV, ltvp->len )

											//all codes between 0xFA00 and 0xFCFF are passed to Hermes
#if (HCF_TYPE) & HCF_TYPE_WPA
{ hcf_16 i, k;
  hcf_8 * dp;
  hcf_8 FAR * sp;

	if ( ltvp->typ == CFG_ADD_TKIP_DEFAULT_KEY || ltvp->typ == CFG_ADD_TKIP_MAPPED_KEY ) {
		if ( ltvp->typ == CFG_ADD_TKIP_MAPPED_KEY ){
			ifbp->IFB_MICKey[0] = k = 1; // in case of CFG_ADD_TKIP_MAPPED_KEY, there is no TxBit, but it will be the TxKey (always), so k=1 !
			sp = (hcf_8 FAR *)&((CFG_ADD_TKIP_MAPPED_KEY_STRCT FAR *)ltvp)->tx_mic_key[0];
		}
		else {
			i = CNV_LITTLE_TO_INT(((CFG_ADD_TKIP_DEFAULT_KEY_STRCT FAR *)ltvp)->tkip_key_id_info);
			k = ( i & KEY_ID ) * 8 + 1;
			if ( i & TX_KEY ) ifbp->IFB_MICKey[0] = k;
			sp = (hcf_8 FAR *)&((CFG_ADD_TKIP_DEFAULT_KEY_STRCT FAR *)ltvp)->tx_mic_key[0];
		}
		dp = (hcf_8*)&ifbp->IFB_MICKey[k];
		for ( i = 0; i < 16; i++ ) {
			*dp++ = *sp++;
		}
	}
	if ( ( ltvp->typ == CFG_REMOVE_TKIP_MAPPED_KEY )	||	// in case of a MAPPED key it is always key 0
		 ( ltvp->typ == CFG_REMOVE_TKIP_DEFAULT_KEY &&
		   ifbp->IFB_MICKey[0] ==
		   CNV_INT_TO_LITTLE(((CFG_REMOVE_TKIP_DEFAULT_KEY_STRCT FAR *)ltvp)->tkip_key_id) * 8 + 1
		 )
		) { ifbp->IFB_MICKey[0] = 0; }		//disable MIC-engine
}
#endif // HCF_TYPE_WPA

#if HCF_DLV || HCF_DLNV
	if ( ltvp->typ == CFG_PROG ) {
#if (HCF_TYPE) & HCF_TYPE_USB
#if !((HCF_TYPE) & HCF_TYPE_HII)
		rc = UNUSendHcfReq( ltvp->typ, &ltvp->val[0], (USHORT)(ltvp->len - 1), NULL);
		/* init is more or less superfluous but not quite. It resets some flags on the dongle side.
		 * For the time being (Nearly Release Time) it seems most prudent not to touch this code.
		 * If USB stays alive and kicking in this dongle form, it makes sense to clean this up
		 * for the next release.
		 */
		if ( ((CFG_PROG_STRCT FAR *)ltvp)->mode == CFG_PROG_STOP && rc == HCF_SUCCESS) rc = init( ifbp );
#else
		rc = download_ubsh2( ifbp, (CFG_PROG_STRCT FAR *)ltvp );
#endif // HCF_TYPE_HII

#else // HCF_TYPE_USB
		rc = download( ifbp, (CFG_PROG_STRCT FAR *)ltvp );
#endif // HCF_TYPE_USB
	} else
#endif // HCF_DLV / HCF_DLNV
	switch (ltvp->typ) {
#if HCF_ASSERT	& 0x4000
	  case CFG_REG_ASSERT_RTNP:											//Register MSF Routines
		ifbp->IFB_AssertRtn = ((CFG_REG_ASSERT_RTNP_STRCT FAR*)ltvp)->rtnp;
//		ifbp->IFB_AssertLvl = ((CFG_REG_ASSERT_RTNP_STRCT FAR *)ltvp)->lvl;
//		ifbp->IFB_AssertLvl = 1;	//already set in hcf_connect		//;? not yet supported so default
		HCFASSERT( DO_ASSERT, 0xCAFE)		//just to proof that the complete assert machinery is working
		break;
#endif // HCF_ASSERT
	  case CFG_REG_INFO_LOG:											//Register Log filter
		ifbp->IFB_RIDLogp = ((CFG_RID_LOG_STRCT FAR*)ltvp)->recordp;
		break;
#if HCF_MB_ON
	  case CFG_REG_MB:													//Register MailBox
		ifbp->IFB_MBp = ((CFG_REG_MB_STRCT FAR *)ltvp)->mb_addr;
		/* if no MB present, size must be 0 for put_info_mb to work correctly */
		ifbp->IFB_MBSize = ifbp->IFB_MBp == NULL ? 0 : ((CFG_REG_MB_STRCT FAR *)ltvp)->mb_size;
		ifbp->IFB_MBWp = ifbp->IFB_MBRp	= 0;
		ifbp->IFB_MBp[0] = 0;											//flag the MailBox as empty
		ifbp->IFB_MBInfoLen = 0;
		HCFASSERT( ifbp->IFB_MBSize >= 60 || ifbp->IFB_MBp == NULL, ifbp->IFB_MBSize )
		HCFASSERT( ( (hcf_32)(ifbp->IFB_MBp) & 0x0001 ) == 0, (hcf_32)ifbp->IFB_MBp )
#endif // HCF_MB_ON
		break;

#if ! ((HCF_TYPE) & HCF_TYPE_USB)
	  case CFG_CMD_NIC:
		OPW( HREG_PARAM_2, ((CFG_CMD_NIC_STRCT FAR *)ltvp)->parm2 );
		OPW( HREG_PARAM_1, ((CFG_CMD_NIC_STRCT FAR *)ltvp)->parm1 );
		rc = cmd_exe( ifbp, ((CFG_CMD_NIC_STRCT FAR *)ltvp)->cmd, ((CFG_CMD_NIC_STRCT FAR *)ltvp)->parm0 );
		((CFG_CMD_NIC_STRCT FAR *)((CFG_CMD_NIC_STRCT FAR *)ltvp))->hcf_stat = (hcf_16)rc;
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->stat = IPW( HREG_STAT );
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->resp0 = IPW( HREG_RESP_0 );
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->resp1 = IPW( HREG_RESP_1 );
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->resp2 = IPW( HREG_RESP_2 );
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->ifb_err_cmd = ifbp->IFB_ErrCmd;
		((CFG_CMD_NIC_STRCT FAR *)ltvp)->ifb_err_qualifier = ifbp->IFB_ErrQualifier;
		break;
#if ((HCF_TYPE) & (HCF_TYPE_HII|HCF_TYPE_AP) ) == HCF_TYPE_AP
  { hcf_16	i;
	  case CFG_NOTIFY:
		if ( ( i = get_fid( ifbp ) ) != 0 ) {
			if ( ( rc = strio_16( ifbp, IO_OUT, i, 0, &ltvp->len, ltvp->len + 1 BE_PAR(2) ) ) == HCF_SUCCESS ) {
				 rc = cmd_exe( ifbp, HCMD_NOTIFY | HCMD_RECL, i );
			}
		}
		break;
  }
#endif // HCF_TYPE_HII / HCF_TYPE_AP
#endif // HCF_TYPE_USB

	  case CFG_CMD_HCF:
#define P ((CFG_CMD_HCF_STRCT FAR *)ltvp)
		HCFASSERT( P->cmd <= CFG_CMD_HCF_RX_MON, P->cmd )
		if ( P->cmd == CFG_CMD_HCF_REG_ACCESS ) {
			OPW( P->mode, P->add_info);
		}
		if ( P->cmd == CFG_CMD_HCF_RX_MON ) {
			 if ( ( ifbp->IFB_Monitor = P->mode ) != 0x0000 ) {
					ifbp->IFB_Monitor = P->add_info;
			 }
		}
#undef P
		break;

	  default:						//pass everything unknown above the "FID" range to the Hermes or Dongle
#if ( (HCF_TYPE) & HCF_TYPE_USB ) == 0
		rc = put_info( ifbp, ltvp );
#else  // HCF_TYPE_USB
#if (HCF_TYPE) & HCF_TYPE_HII
		rc =  uhs_com_rid_ifs( ifbp->IFB_USBIOp, FALSE, ltvp, HCF_USB_RID_IFS_TIMEOUT_MSEC );	// write
#else
		//rc = put_info( ifbp, ltvp );
		rc = UNUSendHcfReq( ltvp->typ, (PUSHORT)&ltvp->val[0], (USHORT)(ltvp->len - 1), NULL );
#endif // HCF_TYPE_HII
#endif // HCF_TYPE_USB
	}
	HCFASSERT( rc == HCF_SUCCESS, rc )
	HCFLOGEXIT( HCF_TRACE_PUT_INFO )
	return rc;
} // hcf_put_info


#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		hcf_rcv_msg
*.DESCRIPTION
*
*.ARGUMENTS
*	int hcf_rcv_msg( IFBP ifbp, DESC_STRCT *descp, int offset )
*
*.RETURNS
*	HCF_SUCCESS		No SSN error ( or HCF_ERR_MIC already reported by hcf_service_nic)
*	HCF_ERR_MIC
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	descp		address of
*	offset	offset 0 corresponds with 802.3 DestAddr field
*			when offset is within lookahead, data is copied from lookahead
*			when offset is just beyond lookahead, data is read directly from RxFS in NIC
*
*	Description:
* Hcf_rcv_msg starts the copy process at the offset requested by the parameter offset, relative to
* HFS_ADDR_DEST, e.g offset 0 starts copying from the Destination Address, the very begin of the 802.3 frame
* message. Offset must either lay within the part of the 802.3 frame as stored by hcf_service_nic in the
* lookahead buffer or be just behind it, i.e. the first byte not yet read.
*
*.DIAGRAM
*
*.NOTICE
*  - by using unsigned int as type for offset, no need to worry about negative offsets
*  - Asserting on being enabled/present is superfluous, since a non-zero IFB_lal implies that hcf_service_nic
*	 was called and detected a Rx-message. A zero IFB_lal will set the buf_cnt field of at least the first
*	 descriptor to zero.
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
hcf_rcv_msg( IFBP ifbp, DESC_STRCT *descp, unsigned int offset )
{
int			rc = HCF_SUCCESS;
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
int			tot_len = ifbp->IFB_RxLen - offset;		//total length
wci_bufp	lap = ifbp->IFB_lap + offset;			//start address in LookAhead Buffer
hcf_16		lal = ifbp->IFB_lal - offset;			//available data within LookAhead Buffer
wci_bufp	cp;
hcf_16		i, j;

	HCFLOGENTRY( HCF_TRACE_RCV_MSG, offset )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT
#if HCF_DDS
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFE, 0xFFFE )
#endif // HCF_DDS
	HCFASSERT( descp, HCF_TRACE_RCV_MSG )
	HCFASSERT( ifbp->IFB_RxLen, HCF_TRACE_RCV_MSG )
	HCFASSERT( ifbp->IFB_RxLen >= offset, ifbp->IFB_RxLen )
	HCFASSERT( ifbp->IFB_RxLen >= offset, offset )
	HCFASSERT( ifbp->IFB_lal >= offset, offset )

	if ( tot_len < 0 ) {
		lal = 0; tot_len = 0;	//suppress all copying activity in the do--while loop
	}
	do {					//loop over all available fragments
		/* obnoxious c:/hcf/hcf.c(1480) : warning C4769: conversion of near pointer to long integer,
		 * so skip
		 * HCFASSERT( ((long)descp & 3 ) == 0, (hcf_16)(long)descp )
		 */
		cp = descp->buf_addr;
		j = min( (hcf_16)tot_len, descp->buf_cntl.buf_dim[DESC_STRCT_SIZE] );	//minimum of "available" and "fit in fragment"
		descp->buf_cntl.buf_dim[DESC_STRCT_CNT] = j;
		tot_len -= j;							//adjust length still to go
		if ( lal ) {							//if lookahead not yet completely copied
			i = min( lal, j );					//minimum of "available" in lookahead and "fit in fragment"
			lal -= i;							//adjust length still available in LookAhead Buffer
			j -= i;								//adjust length still available in current fragment
			while ( i-- ) *cp++ = *lap++;		//;?could be improved by moving words at a time but that is complicated on platforms with alignment requirements;?
		}
		if ( j ) rcv_msg_frag( ifbp, cp, j );	//LookAhead exhausted but still space in fragment, so copy from NIC RAM
	} while ( ( descp = descp->next_desc_addr ) != NULL );
#if (HCF_TYPE) & HCF_TYPE_WPA
	if ( ifbp->IFB_RxFID ) rc = check_mic( ifbp );	//don't report MIC error if hcf_service_nic already consumed all
#endif // HCF_TYPE_WPA
	hcf_action( ifbp, HCF_ACT_RX_ACK );		//only 1 shot to get the data, so free the resources in the NIC
	if (  rc == HCF_SUCCESS && IPW( HREG_SW_0 ) != HCF_MAGIC ) rc = HCF_ERR_NO_NIC;
	HCFASSERT( rc == HCF_SUCCESS, rc )
	HCFLOGEXIT( HCF_TRACE_RCV_MSG )
#else // HCF_TYPE_USB
hcf_8	mic[8];									//area to save rcvd MIC
hcf_16	flag = 0x0000;							//0x01: decapsulation, 0x02: MIC check
hcf_8	*p8 = (hcf_8*)descp->buf_addr;			//char oriented working pointer
#if HCF_ENCAPSULATION == 0x0001
hcf_16	*dp16 = (hcf_16*)&p8[HFS_LEN];			//word oriented destination pointer
hcf_16	*sp16 = (hcf_16*)&p8[HFS_TYPE];			//word oriented source pointer
#endif // HCF_ENCAPSULATION
hcf_32	*p32  = (hcf_32*)&p8[HFS_ADDR_DEST];	//double-word oriented MIC working pointer
int		i;
											/* length without MAC header and without MIC,
											 * but possibly with Encapsulation header
											 */
hcf_16	len  =  p8[HFS_DAT_LEN]+(p8[HFS_DAT_LEN+1]<<8);

											//determine total number of bytes (DA, SA, L, data (no encapsulation, no MIC))
	descp->buf_cntl.buf_dim[DESC_STRCT_CNT] = len + HFS_DAT - HFS_ADDR_DEST;

	//restore asap HCFASSERT( GET_BUF_CNT( descp ) <= GET_BUF_SIZE(descp) , len )
	//restore asap HCFASSERT( GET_BUF_CNT( descp ) <= GET_BUF_SIZE(descp), GET_BUF_SIZE(descp) )

#if HCF_ENCAPSULATION == 0x0001
											//determine decapsulation sub-flag
	i = p8[HFS_STAT+1] & (HFS_STAT_MSG_TYPE>>8);
	if ( ( i == (HFS_STAT_TUNNEL>>8) ) ||
		 ( i == (HFS_STAT_1042>>8) && hcf_encap( (wci_bufp)&p8[HFS_TYPE] ) != ENC_TUNNEL ) ) {
		flag = 0x0001;
											// perform decapsulation, subtract 8 from the length
											// note that MIC is not included in length, hence no comparable correction for MIC
		descp->buf_cntl.buf_dim[DESC_STRCT_CNT] -= 8;
	}
#endif // HCF_ENCAPSULATION
	ifbp->IFB_MICRxRtn = update_mic_dummy;
#if (HCF_TYPE) & HCF_TYPE_WPA
											//if MIC present
//	if ( p8[HFS_STAT] & HFS_STAT_MIC ) {
	if ( *(wci_recordp)&p8[HFS_STAT] & HFS_STAT_MIC ) {
											//.  set MIC sub-flag
		flag |= 0x02;
		ifbp->IFB_MICRxRtn = update_mic;
											//.  initialize LR for MIC calculation
		i = ((p8[HFS_STAT+1] & (HFS_STAT_MIC_KEY_ID>>8))>>00) + 5;// coincidentally no shift needed for i itself*/;
		*(hcf_32*)&ifbp->IFB_MICRx[0] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[i  ]);
		*(hcf_32*)&ifbp->IFB_MICRx[4] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[i+2]);
											//.  save rcvd MIC and add padding to rcvd msg
		for ( i = 0; i < 8; i++ ) {
			mic[i] = p8[ HFS_DAT + len  + i];
			p8[ HFS_DAT + len  + i] = mic_pad[i];
		}

											//.  skip LEN in MIC calculation
											//.  calculate MIC over next 8 bytes (either just 8 data bytes or
											//									  Encapsulation + E-II Type )
		for ( i = 0; i < (12+8)/4; i++ ) {
			MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, *p32++ );
			if ( i == 2 ) {					// splice 4*0 after Len/Type; skip L/T;
				MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, 0x00000000 );
				p32  = (hcf_32*)&p8[HFS_DAT];
			}
		}
	}
#endif // HCF_TYPE_WPA
											//if MIC
											//.  calculate MIC over remaining data + padding
											//if decapsulation
											//.  move remaining data forward in buffer
	for ( i = len / 4; i; i-- ) {
												/* Note that in the previous for-loop already the 8 bytes
												 * after the L/T field, which are counted in len, are processed
												 * without affecting the i in this loop, so this loop consumes
												 * 5 to 8 bytes of the padding inserted after the 802.3 frame.
												 * Due to the up-to 3 bytes truncation caused by dividing len
												 * by 4, the padding is truncated to the appropriate amount of
												 * 0x00 after 0x5a, 0x00, 0x000, 0x000, 0x000
												 */
		MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, *p32++ );
												/* if len == 4*m+3, then moving len/4 times 2 words misses
												 * 3 bytes at the end
												 * since len includes HFS_LEN field and 6 bytes SNAP header,
												 * len is 8 bytes more than really need to be moved,
												 * so at least 5 and most 8 bytes too much are moved in case
												 * of decapsulation
												 */
#if HCF_ENCAPSULATION == 0x0001
		if ( flag & 0x01 ) { *dp16++ = *sp16++; *dp16++ = *sp16++; }
#endif // HCF_ENCAPSULATION
	}
											//check MIC against received MIC
#if (HCF_TYPE) & HCF_TYPE_WPA
	if ( flag & 0x02 ) {
		for ( i = 0; i < 8 && ifbp->IFB_MICRx[i] == mic[i]; i++ ) /* nop */;
		if ( i != 8 ) rc = HCF_ERR_MIC;
	}
#endif // HCF_TYPE_WPA
	ifbp->IFB_RxLen = descp->buf_cntl.buf_dim[DESC_STRCT_CNT];
#endif // HCF_TYPE_USB

	return rc;
} // hcf_rcv_msg
#endif // HCF_DL_ONLY


#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		hcf_send_msg
*.DESCRIPTION	Transfers a message from Host to NIC and/or initiates transmission
*
*.ARGUMENTS
*	int hcf_send_msg( IFBP ifbp, DESC_STRCT *descp, hcf_16 tx_cntl )
*	Card Interrupts disabled
*
*.RETURNS
*	HCF_SUCCESS
*	IFB_DefunctStat
*	HCF_ERR_TIME_OUT
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	descp		pointer to the descriptor list or NULL
*	tx_cntl	indicates MAC-port and (Hermes) options
*
*.DIAGRAM
*4:	for the normal case (i.e. no HFS_TX_CNTL_TX_DELAY option active), a fid is acquired via the
*	routine get_fid.  If no FID is acquired, the remainder is skipped without an error notification.  After
*	all, the MSF is not supposed to call hcf_send_msg when no Resource is available.
*7:	The ControlField of the TxFS is written.  Since strio can only return the fatal Defunct or "No NIC", the
*	return status can be ignored because when it fails, cmd_wait will fail as well.  (see also the note on the
*	need for a return code below).
*	Note that HFS_TX_CNTL has different values for H-I, H-I/SSN and H-II and HFS_ADDR_DEST has different
*	values for H-I (regardless of SSN) and H-II.
*	By writing 17, 1 or 2 ( implying 16, 0 or 1 garbage word after HFS_TX_CNTL) the BAP just gets to
*	HFS_ADDR_DEST for H-I, H-I/SSN and H-II respectively.
*10: if neither encapsulation nor MIC calculation is needed, splitting the first fragment in two does not
*	really help but it makes the flow easier to follow to do not optimize on this difference
*
*	hcf_send_msg checks whether the frame is an Ethernet-II rather than an "official" 802.3 frame.
*	The E-II check is based on the length/type field in the MAC header. If this field has a value larger than
*	1500, E-II is assumed. The implementation of this test fails if the length/type field is not in the first
*	descriptor.  If E-II is recognized, a SNAP header is inserted. This SNAP header represents either RFC1042
*	or Bridge-Tunnel encapsulation, depending on the return status of the support routine hcf_encap.
*
*.NOTICE
*	hcf_send_msg leaves the responsibility to only send messages on enabled ports at the MSF level.
*	This is considered the strategy which is sufficiently adequate for all "robust" MSFs, have the least
*	processor utilization and being still acceptable robust at the WCI !!!!!
*
*	hcf_send_msg does not NEED a return value to report NIC absence or removal during the execution of
*	hcf_send_msg(), because the MSF and higher layers must be able to cope anyway with the NIC being removed
*	after a successful completion of hcf_send_msg() but before the actual transmission took place.
*	To accommodate user expectations the current implementation does report NIC absence.
*	Defunct blocks all NIC access and will (also) be reported on a number of other calls.
*
*	hcf_send_msg does not check for transmit buffer overflow because the Hermes does this protection.
*	In case of a transmit buffer overflow, the surplus which does not fit in the buffer is simply dropped.
*	Note that this possibly results in the transmission of incomplete frames.
*
*	After some deliberation with F/W team, it is decided that - being in the twilight zone of not knowing
*	whether the problem at hand is an MSF bug, HCF buf, F/W bug, H/W malfunction or even something else - there
*	is no "best thing to do" in case of a failing send, hence the HCF considers the TxFID ownership to be taken
*	over by the F/W and hopes for an Allocate event in due time
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
/* ;? figure out what the exact boundaries are to assert the length
 * HCFASSERT( len + sizeof(snap_header) + 2 <= HCF_MAX_MSG + (tx_cntl&HFS_TX_CNTL_MIC)/2, len )
 *			too bad that some of those MS people try to outsmart normal people, otherwise above assert
 *			would compile without errors
 */

int
hcf_send_msg( IFBP ifbp, DESC_STRCT *descp, hcf_16 tx_cntl )
{
int			rc = HCF_SUCCESS;
DESC_STRCT	*p /* = descp*/;		//working pointer
hcf_16		len;					// total byte count
hcf_16		i;

#if (HCF_TYPE) & HCF_TYPE_USB
DESC_STRCT	*p0 = descp;			//fixed pointer to "accumulating" descriptor in case of USB
	usb_simp = (wci_recordp)(descp++->buf_addr);	//!!! note USB ONLY! ++ modifies parameter descp !!!
#else
hcf_16		fid = 0;
PROT_CNT_INI
	HCFASSERT( ifbp->IFB_RscInd || descp == NULL, ifbp->IFB_RscInd )
#endif // HCF_TYPE_USB

	HCFLOGENTRY( HCF_TRACE_SEND_MSG, tx_cntl )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT
#if HCF_DDS
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFE, tx_cntl )
#endif // HCF_DDS
	/* obnoxious c:/hcf/hcf.c(1480) : warning C4769: conversion of near pointer to long integer,
	 * so skip
	 * HCFASSERT( ((long)descp & 3 ) == 0, (hcf_16)(long)descp )
	 */
	HCFASSERT( ( descp != NULL ) ^ ( tx_cntl & HFS_TX_CNTL_TX_DELAY ), tx_cntl )
	HCFASSERT( (tx_cntl & ~HCF_TX_CNTL_MASK ) == 0, tx_cntl )

	IF_NOT_USB( if ( descp ) ifbp->IFB_TxFid = 0; )				//cancel a pre-put message
									/* the following initialization code is redundant for a pre-put message
									 * but moving it inside the "if fid" logic makes the merging with the
									 * USB flow awkward
									 */
	ifbp->IFB_tx_tlen = 0; /* needed independent of TYPE_SSN as long as send_msg_frag is (mis)used to
							  workaround byte I/O */
	ifbp->IFB_MICTxRtn = update_mic_dummy; /* ;? Note that we still pay quite a bit of the expense of calculating MIC */
#if (HCF_TYPE) & HCF_TYPE_WPA
	if ( ifbp->IFB_MICKey[0] ) {		//initialize MIC-engine
		tx_cntl |= HFS_TX_CNTL_MIC | (ifbp->IFB_MICKey[0] - 1)<<8;
		*(hcf_32*)&ifbp->IFB_MICTx[0] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[ifbp->IFB_MICKey[0]  ]);
		*(hcf_32*)&ifbp->IFB_MICTx[4] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[ifbp->IFB_MICKey[0]+2]);
		ifbp->IFB_MICTxRtn = update_mic;
	}
#endif // HCF_TYPE_WPA
	IF_NOT_USB( if ( (fid = ifbp->IFB_TxFid) == 0 && ( fid = get_fid( ifbp ) ) != 0 ) )		/* 4 */
			/* skip the next compound statement if:
			   - pre-put message or
			   - no fid available (which should never occur if the MSF adheres to the WCI)
			 */
	{		// to match the closing curly bracket of above "if" in case of HCF_TYPE_USB

											//calculate total length ;? superfluous if non-DCWA / non-Encapsulation
		len = 0;
		p = descp;
		do len += p->buf_cntl.buf_dim[DESC_STRCT_CNT]; while ( ( p = p->next_desc_addr ) != NULL );
		p = descp;
		HCFASSERT( len <= HCF_MAX_MSG, len )
		i = tx_cntl & HFS_TX_CNTL_MASK;
#if (HCF_TYPE) & HCF_TYPE_USB
		*usb_simp++ = i;	// tx control in front!!
#if   ((HCF_TYPE) & HCF_TYPE_HII)
		*usb_simp++ = 0;	// place holder for length field, before DA SA
#endif // HCF_TYPE_HII
#else  // HCF_TYPE_USB
/*7*/	(void)strio_16( ifbp, IO_OUT, fid, HFS_TX_CNTL, &i, (HFS_ADDR_DEST - HFS_TX_CNTL)/2  BE_PAR(1) );
#endif // HCF_TYPE_USB
#if ! ((HCF_TYPE) & HCF_TYPE_CCX )
		HCFASSERT( p->buf_cntl.buf_dim[DESC_STRCT_CNT] >= 14, p->buf_cntl.buf_dim[DESC_STRCT_CNT] )
											/* assume DestAddr/SrcAddr/Len/Type ALWAYS contained in 1st fragment
											 * otherwise life gets too cumbersome for MIC and Encapsulation !!!!!!!!
		if ( p->buf_cntl.buf_dim[DESC_STRCT_CNT] >= 14 ) {	alternatively: add a safety escape !!!!!!!!!!!! }	*/
/*10*/										//write DA, SA with MIC calculation
#endif // HCF_TYPE_CCX

		send_msg_frag( ifbp, p->buf_addr, 12 );
											//add quad-byte of 0x00 to MIC calculation
		MIC_TX_RTN( (hcf_32*)ifbp->IFB_MICTx, 0x00000000L );

#if (HCF_TYPE) & HCF_TYPE_CCX
		if( ifbp->IFB_CKIP_Enabled == HCF_ACT_CCX_ON /*0x0000*/ ) {
			OPW( HREG_DATA_1, CNV_INT_TO_BIG( len - (( 2 * 6 ))));
		} 
        else if ((GET_BUF_CNT(p) >= 20 ) &&
                 (p->buf_addr[12] == 0xAA) && (p->buf_addr[13] == 0xAA) && 
                 (p->buf_addr[14] == 0x03) && (p->buf_addr[15] == 0x00) &&
                 (p->buf_addr[16] == 0x40) && (p->buf_addr[17] == 0x96) &&
                 (p->buf_addr[18] == 0x00) && (p->buf_addr[19] == 0x00))
        {
            i = 12;

	        HCFASSERT( DO_ASSERT, (CNV_INT_TO_BIG( len - i )));
	        HCFASSERT( DO_ASSERT, (CNV_END_INT( len - i )));

            OPW( HREG_DATA_1, CNV_INT_TO_BIG( len - i ));

            /* need to send out the remainder of the fragment */
			send_msg_frag( ifbp, &p->buf_addr[i], GET_BUF_CNT(p) - i );
        }
        else
#endif // HCF_TYPE_CCX
		{
											//if encapsulation needed
#if HCF_ENCAPSULATION == 0x0001
											//write length (with SNAP-header,Type, without //DA,SA,Length ) no MIC calc.
			if ( ( snap_header[sizeof(snap_header)-1] = hcf_encap( &p->buf_addr[12] ) ) != ENC_NONE ) {
				//I should have made a comment at least stating that the unconditional convert is an intentional act.
				OPW( HREG_DATA_1, CNV_END_INT( len + (sizeof(snap_header) + 2) - ( 2*6 + 2 ) ) );
											//write splice with MIC calculation
				(void)send_msg_frag( ifbp, snap_header, sizeof(snap_header) );
				len += (sizeof(snap_header) + 2 );		/* in fact superfluous except for DCWA for pre-SSN	*/
				i = 12;
			} else
#endif // HCF_ENCAPSULATION
			{
				i = (hcf_16)p->buf_addr[12]*0x100 + p->buf_addr[13]; /*;?probably optimizable by moving body of hcf_encap
															  in line and re-arranging testing and/or CNV_END_INT use*/
				//I should have made a comment at least stating that the unconditional convert is an intentional act.
				OPW( HREG_DATA_1, CNV_END_INT( i ) );
				HCFASSERT( len == i + 14, len + (i<<16) )
				i = 14;
			}
											//complete 1st fragment starting with Type with MIC calculation
			send_msg_frag( ifbp, &p->buf_addr[i], p->buf_cntl.buf_dim[DESC_STRCT_CNT] - i );
		}
											//do the remaining fragments with MIC calculation
		while ( ( p = p->next_desc_addr ) != NULL ) {
			/* obnoxious c:/hcf/hcf.c(1480) : warning C4769: conversion of near pointer to long integer,
			 * so skip
			 * HCFASSERT( ((long)p & 3 ) == 0, (hcf_16)(long)p )
			 */
			send_msg_frag( ifbp, p->buf_addr, p->buf_cntl.buf_dim[DESC_STRCT_CNT] );
		}
											//pad message, finalize MIC calculation and write MIC and DCP to NIC
		send_mic_dcp( ifbp );
		IF_USB(( p0->buf_cntl.buf_dim[DESC_STRCT_CNT] = (char*)usb_simp - p0->buf_addr));
#if !( (HCF_TYPE) & HCF_TYPE_USB )
#if HCF_ASSERT 						||	/*check anyway for test purposes*/  \
	!( (HCF_TYPE) & HCF_TYPE_HII ) 		//DCWA for H-I pre-SSN, DCP for H-I SSN
#if (HCF_TYPE) & HCF_TYPE_WPA
		if ( ifbp->IFB_MICKey[0] ) len += 8;	//  add MIC lenght if applicable
#endif // HCF_TYPE_WPA
		(void)strio_16( ifbp, IO_IN, fid, len + HFS_ADDR_DEST + 1/*compensate odd lenght*/, &i, 1 BE_PAR(1) );
		HCFASSERT( i == 0xCAFE, i | ( len<<16 ) )
#if !( (HCF_TYPE) & (HCF_TYPE_HII|HCF_TYPE_WPA ) ) //DCWA for H-I pre-SSN
		if ( i != 0xCAFE ) {
			// since DCP is not reported HCF_ERR_DCWA might just as well be skipped rc = HCF_ERR_DCWA;
			if ( ifbp->IFB_RscInd == 0 ) ifbp->IFB_RscInd = fid;	//cheap way out to skip transmission
			fid = 0;				//cheap way out to skip transmission
		}
#endif // HCF_TYPE_HII | HCF_TYPE_WPA
#endif // HCF_ASSERT | HCF_TYPE_HII | HCF_TYPE_WPA
		if ( tx_cntl & HFS_TX_CNTL_TX_DELAY ) {
			ifbp->IFB_TxFid = fid;
			fid = 0;
		}
	}
	if ( fid ) {
//check anyway for test purposes
/*16*/	rc = cmd_exe( ifbp, HCMD_BUSY | HCMD_TX | HCMD_DCP | HCMD_RECL, fid );
		ifbp->IFB_TxFid = 0;
			/* probably this (i.e. no RscInd AND "HREG_EV_ALLOC") at this point in time occurs so infrequent,
			 * that it might just as well be acceptable to skip this
			 * "optimization" code and handle that additional interrupt once in a while
			 */
/*20*/	if ( ifbp->IFB_RscInd == 0 ) ifbp->IFB_RscInd = get_fid( ifbp );
#endif // HCF_TYPE_USB
	}
//	HCFASSERT( level::ifbp->IFB_RscInd, ifbp->IFB_RscInd )
	HCFLOGEXIT( HCF_TRACE_SEND_MSG )
	return rc;
} // hcf_send_msg
#endif // HCF_DL_ONLY

#if ! defined HCF_DL_ONLY
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		hcf_service_nic
*.DESCRIPTION	Provides received message, handles (most) Hermes events
*
*.ARGUMENTS
*	int hcf_service_nic( IFBP ifbp, wci_bufp bufp, int len )
*	Card Interrupts disabled
*
*.RETURNS
*	HCF_SUCCESS
*	HCF_ERR_MIC
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*	bufp	address of buffer, sufficiently large to hold the first part of the RxFS up through HFS_TYPE
*	len	length in bytes of buffer specified by bufp
*
*	MSF-accessible fields of Result Block
*	IFB_RxLen			0 or Frame size
*	IFB_MBInfoLen
*	IFB_RscInd
*	IFB_HCF_Tallies
*	IFB_NIC_Tallies
*	IFB_LinkStat
*
*	hcf_service_nic is primarily intended to be part of the Interrupt Service Routine.
*	hcf_service_nic is presumed to neither interrupt other HCF-tasks nor to be interrupted by other HCF-tasks.
*	A way to achieve this is to precede hcf_service_nic as well as all other HCF-tasks with a call to
*	hcf_action to disable the card interrupts and, after all work is completed, with a call to hcf_action to
*	restore (which is not necessarily the same as enabling) the card interrupts.
*	In case of a polled environment, it is assumed that the MSF programmer is sufficiently familiar with the
*	specific requirements of that environment to translate the interrupt strategy to a polled strategy.
*
*	hcf_service_nic services the following Hermes events:
*		HREG_EV_INFO		Asynchronous Information Frame
*		HREG_EV_INFO_DROP	WMAC did not have sufficient RAM to build Unsolicited Information Frame
*		HREG_EV_ALLOC		Asynchronous part of Allocation/Reclaim completed while out of resources at
*							completion of hcf_send_msg/notify
*		HREG_EV_RX			the detection of the availability of received messages
*							including WaveLAN Management Protocol (WMP) message processing
*
*
*	A received message can be either "NOS" messages or "WMP" (aka DUIF) messages.
*	A WMP message is recognized by the Hermes and reported as such in the Status field of the Rx Frame
*	Structure. A number of WMP messages are handled by the Hermes and are never passed to the Host at all.
*	The error free WMP messages which are passed to the Host are stored in the MailBox (assuming one with
*	sufficient free space is available). As a side effect, when IFB_MBInfoLen is zero, it is updated to
*	reflect the size of the MailBox Information Block used to store the WMP message.  ;? If Monitor Mode is
*	active, a WMP message SHOULD be considered a NOS message. Current implementation moves all ERRORFREE
*	messages (also those addressed to other stations) into the MailBox. Frames containing errors are passed to
*	the MSF as is (without decapsulation).
*
*	An available NOS messages is a message which:
*	 -	is received by the LAN Controller with an OK status and which is not an WMP message
*	 -	All messages (;?including WMP messages and messages with an error status) when Monitor Mode is enabled
*
*	If a message is available, its length is reflected by the IFB_RxLen field of the IFB. This length reflects
*	the data itself and the Destination Address, Source Address and DataLength/Type field but not the
*	SNAP-header in case of decapsulation by the HCF.  If no message is available, IFB_RxLen is zero.
*
*	**Rx Buffer free strategy
*	When hcf_service_nic reports the availability of a message, the MSF can access that message by means of
*	hcf_rcv_msg. It must be prevented that the LAN Controller writes new data in the NIC buffer before the MSF
*	is finished with the current message. The NIC buffer is returned to the LAN Controller when:
*	 - the complete frame fits in the lookahead buffer
*	 - hcf_rcv_msg is called or
*	 - hcf_action with HCF_ACT_RX is called or
*	 - hcf_service_nic is called again
*	It can be reasoned that hcf_action( INT_ON ) should not be given before the MSF has completely processed
*	a reported Rx-frame. The reason is that the INT_ON action is guaranteed to cause a (Rx-)interrupt (the
*	MSF is processing a Rx-frame, hence the Rx-event bit in the Hermes register must be active). This
*	interrupt will cause hcf_service_nic to be called, which will cause the ack-ing of the "last" Rx-event
*	to the Hermes, causing the Hermes to discard the associated NIC RAM buffer.
*
*
*.DIAGRAM
*1:	IFB_LinkStat is cleared, if a LinkStatus frame is received, IFB_LinkStat will be updated accordingly
*	by isr_info.
*2: IFB_RxLen must be cleared before the NIC presence check otherwise:
*	 -	this value may stay non-zero if the NIC is pulled out at an inconvenient moment.
*	 -	the RxAck on a zero-FID needs a zero-value for IFB_RxLen to work
*	 Note that as side-effect of the hcf_action call, the remainder of Rx related info is re-initialized as
*	 well.
*4:	In case of Defunct mode, the information supplied by Hermes is unreliable, so the body of
*  hcf_service_nic is skipped
*	To prevent that hcf_service_nic reports bogus information to the MSF with all - possibly difficult to
*	debug - undesirable side effects, it is paramount to check the NIC presence based on the Hermes register
*	HREG_SW_0.
*	Note that in polled environments Card Removal is not detected by INT_OFF which makes the check in
*	hcf_service_nic even more important
*8:	The event status register of the Hermes is sampled
*	The assert checks for unexpected events.
*	 - HREG_EV_INFO_DROP is explicitly excluded from the acceptable HREG_EV_STAT bits because it indicates
*	   a too heavily loaded system.
*	 - HREG_EV_ACK_REG_READY is 0x0000 for H-I (and hopefully H-II.5)
*	 - HREG_EV_RES is 0x0000 for H-II
*	 - HREG_EV_TX_OK and HREG_EV_TX_EX are accepted if and only if they are set in the HCF_EX_INT definition
*	   at compile time.
*	The following activities are handled:
*	 -	Alloc events are handled by hcf_send_msg (and notify). Only if there is no "spare" resource, the
*		alloc event is superficially serviced by hcf_service_nic to create a pseudo-resource with value
*		0x001. This value is recognized by get_fid (called by hcf_send_msg and notify) where the real
*		TxFid is retrieved and the Hermes is acked and - hopefully - the "normal" case with a spare TxFid
*		in IFB_RscInd is restored.
*	 -	Info drop events are handled by incrementing a tally
*	 -	LinkEvent (including solicited and unsolicited tallies) are handled by procedure isr_info.
*	-	TxEx/TxOK (if selected at compile time) is handled by copying the significant part of the TxFS
*		into the mailbox, to allow the MSF to determine the MAC address associated with the failure/success.
*		Note the complication of the zero-FID protection sub-scheme in DAWA.
*	Note, the Ack of all of above events is handled at the end of hcf_service_nic
*10: ;?some explanation of the alloc processing still to be added
*16: If an Rx-frame is available, first the FID of that frame is read, including the complication of the
*	zero-FID protection sub-scheme in DAWA. Note that such a zero-FID is acknowledged at the end of
*	hcf_service_nic and that this depends on the IFB_RxLen initialization in the begin of hcf_service_nic.
*	The Assert validates the HCF assumption about Hermes implementation upon which the range of
*	Pseudo-RIDs is based.
*	Then the control fields up to the start of the 802.3 frame are read from the NIC into the lookahead buffer.
*	The status field is converted to native Endianess.
*	The length is, after implicit Endianess conversion if needed, and adjustment for the 14 bytes of the
*	802.3 MAC header, stored in IFB_RxLen.
*	In MAC Monitor mode, 802.11 control frames with a TOTAL length of 14 are received, so without this
*	length adjustment, IFB_RxLen could not be used to distinguish these frames from "no frame".
*	No MIC calculation processes are associated with the reading of these Control fields.
*	If the Hermes frame status reflects an error - which can only occur in promiscuous mode - none of the
*	following tests is true, hence the frame is not further processed and control is passed back to the MSF
*20: WMP messages are processed by copying them to the MailBox.
*	In Monitor mode all messages are copied to the Mailbox and are dropped as far as the Protocol Stack
*	is concerned
*	Note: No filtering on Hermes engineering facilities, hence these frames (which should only be observed
*	on Agere premises) end up in the MailBox !!!!!!!!
*24: If not in Monitor mode and if the message is not a WMP message, the "normal" Rx-flow continues with
*	initializing the work-variables.
*26: This length test feels like superfluous robustness against malformed frames, but it turned out to be
*	needed in the real (hostile) world.
*	The decapsulation check needs sufficient data to represent DA, SA, L, SNAP and Type which amounts to
*	22 bytes. In MAC Monitor mode, 802.11 control frames with a smaller length are received. To prevent
*	that the implementation goes haywire, a check on the length is needed.
*	The actual decapsulation takes place on the fly in the copying process by overwriting the SNAP header.
*	Note that in case of decapsulation the SNAP header is not passed to the MSF, hence IFB_RxLen must be
*	compensated for the SNAP header length.
*	The 22 bytes needed for decapsulation are (more than) sufficient for the exceptional handling of the
*	MIC algorithm of the L-field (replacing the 2 byte L-field with 4 0x00 bytes).
*28: If Hermes reported MIC-presence, than the MIC engine is initialized with the non-dummy MIC calculation
*	routine address and appropriate key. Note that "coincidentally" the bit positions used in HFS_STAT
*	correspond with the offset of the key in IFB_MICKey
*30: The 12 in the no-SSN branch corresponds with the rcv_msg_frag, the 2 with the IPW of the SSN branch
*34: The 8 bytes after the DA, SA, L are read and it is checked whether decapsulation is needed i.e.:
*	  - the Hermes reported Tunnel encapsulation or
*	  - the Hermes reported 1042 Encapsulation and hcf_encap reports that the HCF would not have used
*		1042 as the encapsulation mechanism
*36: The Type field is the only word kept (after moving) of the just read 8 bytes, it is moved to the
*	L-field.  The original L-field and 6 byte SNAP header are discarded, so IFB_RxLen and buf_addr must
*	be adjusted by 8.
*40: Determine how much of the frame (starting with DA) fits in the Lookahead buffer, then read the not-yet
*	read data into the lookahead buffer.
*	If the lookahead buffer contains the complete message, check the MIC. The majority considered this
*	I/F more appropriate then have the MSF call hcf_rcv_msg only to check the MIC.
*44: Since the complete message is copied from NIC RAM to PC RAM, the Rx can be acknowledged to the Hermes
*	to optimize the flow ( a better chance to get new Rx data in the next pass through hcf_service_nic ).
*	This acknowledgement can not be done via hcf_action( HCF_ACT_RX_ACK ) because this also clears
*	IFB_RxLEN thus corrupting the I/F to the MSF.
*54: Limiting the number of places where the F/W is acked (e.g. the merging of the Rx-ACK with the other ACKs),
*	is supposed to diminish the potential of race conditions in the F/W.
*	Note 1: The CMD event is acknowledged in cmd_cmpl
*	Note 2: HREG_EV_ACK_REG_READY is 0x0000 for H-I (and hopefully H-II.5)
*	Note 3: The ALLOC event is acknowledged in get_fid (except for the initialization flow)
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
hcf_service_nic( IFBP ifbp, wci_bufp bufp, unsigned int len )
{

int			rc = HCF_SUCCESS;
hcf_16		stat;
wci_bufp	buf_addr;

	HCFLOGENTRY( HCF_TRACE_SERVICE_NIC, ifbp->IFB_IntOffCnt )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT
#if HCF_DDS
	HCFASSERT( ifbp->IFB_IntOffCnt != 0xFFFE, len )
#endif // HCF_DDS
	HCFASSERT( bufp, len )
#if HCF_ENCAPSULATION == 0x0001
	HCFASSERT( len >= HFS_DAT + 2 + sizeof(snap_header), len )
#else
	HCFASSERT( len >= HFS_DAT + 2, len )
#endif // HCF_ENCAPSULATION

	ifbp->IFB_LinkStat = 0;																				/* 1*/
	hcf_action( ifbp, HCF_ACT_RX_ACK );																	/* 2*/
	if ( !(ifbp->IFB_CardStat & CARD_STAT_DEFUNCT) && IPW( HREG_SW_0 ) == HCF_MAGIC ) {					/* 4*/
		stat = IPW( HREG_EV_STAT );																		/* 8*/
#if (HCF_TYPE) & HCF_TYPE_HII5
		HCFASSERT( !( stat & ~( HREG_EV_TICK | HREG_EV_RES | HREG_EV_ACK_REG_READY | HREG_EV_SLEEP_REQ	|
								/* HREG_EV_INFO_DROP |*/ HREG_EV_INFO | HREG_EV_CMD |
								HREG_EV_LPESC | HREG_EV_RDMAD | HREG_EV_TDMAD | HREG_EV_RXDMA | HREG_EV_TXDMA |
								  HREG_EV_ALLOC | HCF_EX_INT | HREG_EV_RX ) ), stat )
#else
		HCFASSERT( !( stat & ~( HREG_EV_TICK | HREG_EV_RES | HREG_EV_ACK_REG_READY | HREG_EV_SLEEP_REQ	|
								/* HREG_EV_INFO_DROP |*/ HREG_EV_INFO | HREG_EV_CMD |
								HREG_EV_ALLOC | HCF_EX_INT | HREG_EV_RX ) ), stat )
		HCFASSERT( !( stat &  HREG_EV_TX_EX ), stat )
#endif // !HCF_TYPE_HII5
		if ( stat & HREG_EV_ALLOC && ifbp->IFB_RscInd == 0 ) ifbp->IFB_RscInd = 1;						/*10*/
		IF_TALLY( if ( stat & HREG_EV_INFO_DROP ) ifbp->IFB_HCF_Tallies.NoBufInfo++; )
		if ( stat & HREG_EV_INFO ) isr_info( ifbp );
#if HCF_EX_INT & (HCF_EX_INT_TX_EX | HCF_EX_INT_TX_OK)
		if ( stat & (HREG_EV_TX_EX | HREG_EV_TX_OK) &&
			 ( ifbp->IFB_MB_FID = IPW( HREG_TX_COMPL_FID ) ) != 0 /*DAWA*/ ) {
			put_info_mb( ifbp, CFG_EX_INT_TX, NULL, HFS_DAT/2 + 1 );
		}
#endif // HCF_EX_INT
/*16*/	if ( stat & HREG_EV_RX && ( ifbp->IFB_RxFID = IPW( HREG_RX_FID ) ) != 0 ) { //if 0 then DAWA_ACK( RX );
			DAWA_ZERO_FID( HREG_RX_FID )
			HCFASSERT( ifbp->IFB_RxFID < CFG_TX_FRAME, ifbp->IFB_RxFID)
			(void)strio_16(ifbp, IO_IN, ifbp->IFB_RxFID, 0, (wci_recordp)bufp, HFS_ADDR_DEST/2 BE_PAR(1) );
			ifbp->IFB_lap = buf_addr = bufp + HFS_ADDR_DEST;
			ifbp->IFB_RxLen = (hcf_16)(bufp[HFS_DAT_LEN] + (bufp[HFS_DAT_LEN+1]<<8) + 2*6 + 2);
#if HCF_MB_ON
/*20*/		if ( (*(wci_recordp)&bufp[HFS_STAT] & ( HFS_STAT_MSG_TYPE | HFS_STAT_ERR ) ) == HFS_STAT_WMP_MSG
#if HCF_MONITOR_MODE
				 || (ifbp->IFB_Monitor && ( ifbp->IFB_RxLen = min( ifbp->IFB_Monitor, ifbp->IFB_RxLen ) ) != 0 )
#endif // HCF_MONITOR_MODE
			) {
				ifbp->IFB_MB_FID = ifbp->IFB_RxFID;
				put_info_mb( ifbp, CFG_WMP, NULL, (hcf_16)DIV_BY_2(ifbp->IFB_RxLen + HFS_ADDR_DEST + 2) );
				ifbp->IFB_RxFID = ifbp->IFB_RxLen = 0;		//Rx_Ack at the end of hcf_service_nic
			} else
/*24*/
#endif // HCF_MB_ON
			{
				ifbp->IFB_MICRxRtn = update_mic_dummy;
				ifbp->IFB_rx_tlen = 0; //;?needed as long as you do not really skip MIC calculation
/*26*/			if ( ifbp->IFB_RxLen >= 2*6 + 2 + 6 + 2 ) {
hcf_16 i;
#if (HCF_TYPE) & HCF_TYPE_WPA
													//if MIC received
/*28*/				if ( *(wci_recordp)&bufp[HFS_STAT] & HFS_STAT_MIC ) {
													//.  initialize MIC housekeeping
						ifbp->IFB_MICRxRtn = update_mic;
						/* coincidentally no shift needed for i itself */
						i = ( (*(wci_recordp)&bufp[HFS_STAT] & HFS_STAT_MIC_KEY_ID ) >>8 ) + 5;
						*(hcf_32*)&ifbp->IFB_MICRx[0] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[i  ]);
						*(hcf_32*)&ifbp->IFB_MICRx[4] = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICKey[i+2]);
					}
													//.  get DA,SA and calculate MIC
					rcv_msg_frag( ifbp, buf_addr, 12 );
													//.  add quad-byte of 0x00 to MIC calculation
					MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, 0x00000000L );
													//.  get Length without MIC calculation
					*(wci_recordp)(buf_addr+12) = IPW( HREG_DATA_1 );
#else
													//.  get DA,SA and Len/Type
/*30*/				rcv_msg_frag( ifbp, buf_addr, 12 + 2 );
#endif // HCF_TYPE_WPA
					buf_addr += 12 + 2;
					// get next 8 bytes and calculate MIC over it
					/* (either Encapsulation + E-II Type or just 8 data bytes */
#if ((HCF_TYPE) & HCF_TYPE_CCX )
//!!be carefull do not use positive test on HCF_ACT_CCX_OFF, because IFB_CKIP_Enabled is intially 0
					if( ifbp->IFB_CKIP_Enabled != HCF_ACT_CCX_ON )
#endif // HCF_TYPE_CCX
					{
#if HCF_ENCAPSULATION == 0x0001
/*34*/					rcv_msg_frag( ifbp, buf_addr, 8 );
						buf_addr += 8;
						i = *(wci_recordp)&bufp[HFS_STAT] & ( HFS_STAT_MSG_TYPE | HFS_STAT_ERR );
						if ( i == HFS_STAT_TUNNEL ||
							 ( i == HFS_STAT_1042 && hcf_encap( (wci_bufp)&bufp[HFS_TYPE] ) != ENC_TUNNEL ) ) {
														//.  copy E-II Type to 802.3 LEN field
/*36*/						bufp[HFS_LEN  ] = bufp[HFS_TYPE  ];
							bufp[HFS_LEN+1] = bufp[HFS_TYPE+1];
														//.  discard Snap by overwriting with data
							ifbp->IFB_RxLen -= (HFS_TYPE - HFS_LEN);
							buf_addr -= ( HFS_TYPE - HFS_LEN );
						}
#endif // HCF_ENCAPSULATION
					}
				}
				HCFASSERT( (int)ifbp->IFB_RxLen <= HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2, ifbp->IFB_RxLen )
				HCFASSERT( (int)ifbp->IFB_RxLen <= HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2, ifbp->IFB_RxFID )
/*40*/			ifbp->IFB_lal = min( (hcf_16)(len - HFS_ADDR_DEST), ifbp->IFB_RxLen );
				// cbz
				HCFASSERT( (int)ifbp->IFB_RxLen <= HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2, (HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2) )
				HCFASSERT( (int)ifbp->IFB_RxLen <= HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2, (len - HFS_ADDR_DEST) )
				HCFASSERT( (int)ifbp->IFB_RxLen <= HCF_MAX_MSG + (bufp[HFS_STAT]&HFS_STAT_MIC)/2, ifbp->IFB_lal - ( buf_addr - ( bufp + HFS_ADDR_DEST ) ) )
				// cbz
				rcv_msg_frag( ifbp, buf_addr, ifbp->IFB_lal - (int)( buf_addr - ( bufp + HFS_ADDR_DEST ) ) );
#if (HCF_TYPE) & HCF_TYPE_WPA
				if ( ifbp->IFB_lal == ifbp->IFB_RxLen ) rc = check_mic( ifbp );
#endif // HCF_TYPE_WPA
/*44*/			if ( len - HFS_ADDR_DEST >= ifbp->IFB_RxLen ) ifbp->IFB_RxFID = 0;
				/* IFB_RxFID is cleared, so  you do not get another Rx_Ack at next entry of hcf_service_nic */
				else stat &= (hcf_16)~HREG_EV_RX;	//don't ack Rx if processing not yet completed
			}
		}
/*54*/	stat &= (hcf_16)~( HREG_EV_SLEEP_REQ | HREG_EV_CMD | HREG_EV_ACK_REG_READY | HREG_EV_ALLOC );
#if (HCF_TYPE) & HCF_TYPE_HII5
		stat &= (hcf_16)~( HREG_EV_LPESC | HREG_EV_RDMAD | HREG_EV_TDMAD | HREG_EV_RXDMA | HREG_EV_TXDMA );
#endif // HCF_TYPE_HII5
		if ( stat ) DAWA_ACK( stat );	/*DAWA*/
	}
	HCFLOGEXIT( HCF_TRACE_SERVICE_NIC )
	return rc;
} // hcf_service_nic
#endif // HCF_TYPE_USB
#endif // HCF_DL_ONLY


/************************************************************************************************************
************************** H C F   S U P P O R T   R O U T I N E S ******************************************
************************************************************************************************************/


#if ((HCF_TYPE) & (HCF_TYPE_USB | HCF_TYPE_HII) ) == 0
///Prepares/terminates access via AUX-mechanism to the NIC RAM.
/****/
/**
*
*.MODULE		aux_cntl
*.DESCRIPTION	Prepares/terminates access via AUX-mechanism to the NIC RAM.
*
*.ARGUMENTS
*	int aux_cntl( IFBP ifbp, int cmd )
*
*.RETURNS
*	HCF_SUCCESS
*	HCF_ERR_DEFUNCT_AUX
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*	cmd	HREG_CNTL_AUX_ENA_CNTL	enables AUX-port access mechanism
*		HREG_CNTL_AUX_DIS_CNTL	disables ,,  ,,     ,,      ,,
*
*	Description:
*	Prepares/terminates access via AUX-mechanism to the NIC RAM.
*
*.DIAGRAM
*
*  5:	the code "OUT_PORT_WORD( i, IN_PORT_WORD( i ) & ~HREG_CNTL_AUX_ENA | HREG_CNTL_AUX_ENA_CNTL );"
*	could just as well have been written as:
*				t = IN_PORT_WORD( i ) & ~HREG_CNTL_AUX_ENA;
*				OUT_PORT_WORD( i, t | HREG_CNTL_AUX_ENA_CNTL );
*	The compiler is in "default release" configuration (minimal size, full optimization) clever enough to see
*	that in the "clear" form, t is overwritten with TickIni after its only use in OUT_PORT_WORD so it is not
*	saved in [bp-2] (which happens to be the location of t).  However the compiler moves the value of t into
*	register cx and out again for no good reason. In the obscure form the compiler leaves the value of t in
*	register ax throughout the process.  I guess this is typical Micro$oft.
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
aux_cntl( IFBP ifbp, hcf_16 cmd )
{

int		rc = HCF_SUCCESS;
PROT_CNT_INI
hcf_io	io_port = ifbp->IFB_IOBase + HREG_CNTL;

	OPW( HREG_PARAM_0, AUX_MAGIC_0 );
	OPW( HREG_PARAM_1, AUX_MAGIC_1 );
	OPW( HREG_PARAM_2, AUX_MAGIC_2 );
	OUT_PORT_WORD( io_port,
				   ( IN_PORT_WORD( io_port ) & (hcf_16)~(HREG_CNTL_AUX_ENA|HREG_CNTL_AUX_DSD) ) | cmd );/* 5 */
	cmd = (hcf_16)(cmd & HREG_CNTL_AUX_ENA_CNTL ? HREG_CNTL_AUX_ENA_STAT : HREG_CNTL_AUX_DIS_STAT);
	HCF_WAIT_WHILE( (IN_PORT_WORD( io_port ) & HREG_CNTL_AUX_ENA) != cmd );
#if HCF_PROT_TIME
	if ( prot_cnt == 0 ) {
		IF_TALLY( ifbp->IFB_HCF_Tallies.MiscErr++; )
		rc = ifbp->IFB_DefunctStat = HCF_ERR_DEFUNCT_AUX;
		ifbp->IFB_CardStat |= CARD_STAT_DEFUNCT;
	}
#endif // HCF_PROT_TIME
	return rc;
} // aux_cntl
#endif // HCF_TYPE_USB / HCF_TYPE_HII


#if ! ((HCF_TYPE) & HCF_TYPE_USB)
#if HCF_PROT_TIME
/************************************************************************************************************
*
*.MODULE		calibrate
*.DESCRIPTION	calibrates the S/W protection counter against the Hermes Timer tick
*
*.ARGUMENTS
*	void calibrate( IFBP ifbp )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*
*	Description: calibrates the S/W protection counter against the Hermes Timer tick
*	IFB_TickIni is the value used to initialize the S/W protection counter such that the expiration period
*	more or less independent of the processor speed. If IFB_TickIni is not yet calibrated, it is done now.
*	This calibration is "reasonably" accurate because the Hermes is in a quiet state as a result of the
*	Initialize command.
*
*.DIAGRAM
*
*1:	IFB_TickIni is initialized at INI_TICK_INI by hcf_connect. If calibrate succeeds, IFB_TickIni is
*	guaranteed to be changed. As a consequence there will be only 1 shot at calibration (regardless of the
*	number of init calls) under normal circumstances.
*2:	Calibration is done HCF_PROT_TIME times. This diminish the effects of jitter and interference,
*	especially in a pre-emptive environment.
*	Each cycle is limited to at most INI_TICK_INI samples of the TimerTick status of the Hermes.
*	Since the start of calibrate is unrelated to the Hermes Internal Timer, the first interval may last from 0
*	to the normal interval, all subsequent intervals should be the full length of the Hermes Tick interval.
*	The Hermes Timer Tick is not reprogrammed by the HCF, hence it is running at the default of 10 k
*	microseconds.
*3:	Acknowledge the Timer Tick Event. For the first time through the loop there may or there may not be
*	a Timer Tick Event, this does not influence the observation made under 2 above.
*4:	If the Timer Tick Event is continuously up (prot_cnt still has the value INI_TICK_INI) or no Timer Tick
*	Event occurred before the protection counter expired, reset IFB_TickIni to INI_TICK_INI,
*	set the defunct bit of IFB_CardStat (thus rendering the Hermes inoperable) and exit the calibrate routine.
*8:	ifbp->IFB_TickIni *= HCF_PROT_TIME/8;	yields references to LIB routines for some compilers
*
*.NOTICE
*  o  Although there are a number of viewpoints possible, calibrate() uses now as error strategy that a single
*	failure of the Hermes TimerTick is considered fatal.
*  o	If the Timer Tick Event is ALWAYS present (e.g. due to an error, we read 0xFFFF continuously),
*	IFB_TickIni stays zero so neither cmd_exe nor strio will be able to execute. This is considered
*	adequate protection against this unlikely event.
*  o  The Hermes does not restart nor reset its timing logic at an Initialize command. This implies that the
*	very first timing period measured after an Initialize command may be anything between 0 and whatever
*	the Hermes Timer was programmed by means of CFG_TICK_TIME. In the past the HCF programmed the Hermes
*	timer to 1 second. This definitely jeopardizes the calibration as implemented in calibrate().
*	By executing calibrate() only once in the lifetime of the driver would (supposedly) solve the problem.
*	This solution is not robust, due to the fragilIty of the Hermes implementation of the Initialize, Disable
*	and Enable commands as well as due to MSF sequences of hcf_connect, hcf_disconnect.
*  o	There is no hard and concrete time-out value defined for Hermes activities. The 2.5 seconds is believed
*	to be sufficiently "relaxed" for real life and to be sufficiently short to be still useful in an
*	environment with humans.
*  o	To diminish the chance that in a pre-emptive environment IFB_TickIni is calibrated too low because the HCF
*	just happens to loose control during this calibration, the calibration is performed 10 times and the
*	largest value is used.
*  o	In case of failure, IFB_TickIni ends up as INI_TICK_INI, which renders the HCF inoperable
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
void
calibrate( IFBP ifbp )
{

int		cnt = HCF_PROT_TIME_CNT;	//8;
hcf_32	prot_cnt;

	HCFTRACE( ifbp, HCF_TRACE_CALIBRATE );
	if ( ifbp->IFB_TickIni == INI_TICK_INI ) {													/* 1 */
/*2*/	ifbp->IFB_TickIni = 0;
			while ( cnt-- ) {
				prot_cnt = INI_TICK_INI;
/*3*/			OPW( HREG_EV_ACK, HREG_EV_TICK );
				while ( (IPW( HREG_EV_STAT ) & HREG_EV_TICK) == 0 && --prot_cnt ) {
					ifbp->IFB_TickIni++;
				}
/*4*/			if ( prot_cnt == 0 || prot_cnt == INI_TICK_INI ) {
					ifbp->IFB_TickIni = INI_TICK_INI;
					ifbp->IFB_DefunctStat = HCF_ERR_DEFUNCT_TIMER;
					ifbp->IFB_CardStat |= CARD_STAT_DEFUNCT;
					HCFASSERT( DO_ASSERT, ifbp->IFB_CardStat )
				}
			}
/*8*/	ifbp->IFB_TickIni <<= HCF_PROT_TIME_SHFT;
	}
	HCFTRACE( ifbp, HCF_TRACE_CALIBRATE | HCF_TRACE_EXIT );
} // calibrate
#endif // HCF_PROT_TIME
#endif // HCF_TYPE_USB

#if ( (HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == HCF_TYPE_USB
#if defined MSF_COMPONENT_ID
/************************************************************************************************************
*
*.MODULE			check_comp_usb
*.DESCRIPTION	Checks compatibility of the USB Bootloader and USB Board I/F
*
*.ARGUMENTS
*	int check_comp_usb( IFBP ifbp )
*
*.RETURNS
*	non-zero	compatible
*	0x0000	incompatible
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*
*  Description: Check_comp is a support routine to check the compatibility of the HCF as actor
*	with the USB dongle for the USB Bootloader and USB Board I/F
*
*.DIAGRAM
*  The code is considered self explanatory
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int check_comp_usb( IFBP ifbp )
{

int					rc = HCF_ERR_INCOMP_PRI;
CFG_SUP_RANGE_STRCT sup;

	ifbp->IFB_CardStat = CARD_STAT_INCOMP_PRI;
	sup.len = sizeof(CFG_SUP_RANGE_STRCT)/sizeof(hcf_16) - 1;
	sup.typ = CFG_UBI_SUP_RANGE;
	if ( hcf_get_info( ifbp, (LTVP)&sup ) == HCF_SUCCESS &&
		 mmd_check_comp( (CFG_RANGES_STRCT*)&cfg_drv_act_ranges_ubi, &sup ) != NULL ) {
		sup.len = sizeof(CFG_RANGES_STRCT)/sizeof(hcf_16) - 1;
		sup.typ = CFG_UDI_SUP_RANGE;
		if ( hcf_get_info( ifbp, (LTVP)&sup ) == HCF_SUCCESS &&
			 mmd_check_comp( (CFG_RANGES_STRCT*)&cfg_drv_act_ranges_udi, &sup ) != NULL ) {
			rc = HCF_SUCCESS;
			ifbp->IFB_CardStat = 0;
		}
	}
	HCFASSERT( rc == HCF_SUCCESS, rc)
	return rc;
} // check_comp_usb

#endif // HCF_TYPE_USB / HCF_TYPE_HII
#endif // MSF_COMPONENT_ID


#if ! defined HCF_DL_ONLY
#if (HCF_TYPE) & HCF_TYPE_WPA && ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		check_mic
*.DESCRIPTION	verifies the MIC of a received non-USB frame
*
*.ARGUMENTS
*	int check_mic( IFBP ifbp )
*
*.RETURNS
*	HCF_SUCCESS
*	HCF_ERR_MIC
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*
*	Description:
*4:	test whether or not a MIC is reported by the Hermes
*6:	presume accumulator IFB_rx_32 contains either 4 garbage bytes or 1, 2 or 3 final data bytes and 3, 2 or 1
*	bytes of the received MIC.  The loop:
*	 - saves the 4 garbage bytes or the 3, 2, or 1 MIC bytes in x8.
*	 - append 4, 3, 2, or 1 bytes of the padding sequence 0x5A000000 to the final data bytes (if any) in IFB_rx_32
*	In case 4 garbage bytes are saved in x8, the index i is cleared, causing the MIC bytes read at point 12 to
*	be deposited starting at the begin of x8. In the other 3 cases, the index i is not modified, causing the
*	MIC bytes to be appended.
*10: calculate the MIC over the first 4 padding bytes or over the last data byte(s) + padding
*11: calculate the MIC over 4 more 0x00 padding bytes
*	Net effect: 0x5a and at least 4 times 0x00 and at most 7 times 0x00 is appended to the message.
*12: in the while loop at point 6, at most 3 bytes of the received MIC are read, now read the missing
*	5, 6, 7 or 8 bytes.
*	Since words are read this corresponds with 3, 3, 4 or 4 words read at byte offset 3, 2, 1 or 0 in x8[],
*	resulting in a total of 9, 8, 9 and 8 bytes in x8[]
*14: the calculated MIC and the received MIC are compared, the return status is set when there is a mismatch
*
*.DIAGRAM
*
*.NOTICE
* The check_mic algorithm is intimately interwoven with lookahead mechanism (IFB_lap) and byte-gathering logic
* (IFB_rx_tlen) that despite the commonality with the USB-specific flow (splicing in the padding and the
* actual compare itself), the USB-specific check is integrated into hcf_rcv_msg.
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
check_mic( IFBP ifbp )
{
int		rc = HCF_SUCCESS;
int		i = 0;
union { hcf_32 x32[2]; hcf_16 x16[4]; hcf_8 x8[8+1]; } x;	//* area to save rcvd MIC + 1 extra (see *12 )
hcf_16	tmpvar;

	if ( *(wci_recordp)&ifbp->IFB_lap[-HFS_ADDR_DEST] & HFS_STAT_MIC ) {						/* 4 */
/*6*/	while ( ifbp->IFB_rx_tlen < 4 ) {
			x.x8[i] = ifbp->IFB_rx_32[ifbp->IFB_rx_tlen];
				ifbp->IFB_rx_32[ifbp->IFB_rx_tlen++] = mic_pad[i++];
			}
		i &= 0x03;	//if ifbp->IFB_rx_tlen was 0, then still 8 MIC bytes must be read
#if HCF_BIG_ENDIAN
/*10*/	MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx,
					(ifbp->IFB_rx_32[0] << 16)+
					(ifbp->IFB_rx_32[1] << 24) +
					(ifbp->IFB_rx_32[2]      ) +
					(ifbp->IFB_rx_32[3] <<  8)
				  );
#else
/*10*/	MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, *(hcf_32*)&ifbp->IFB_rx_32[0] );
#endif // HCF_BIG_ENDIAN
/*11*/	MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, 0x00000000 );
/*12*/	while ( i < 8 ){
			tmpvar = IPW( HREG_DATA_1 );
			x.x8[i++] = (hcf_8)(tmpvar&0xFF);
			x.x8[i++] = (hcf_8)(tmpvar>>8);
		}
///*14*/	for ( i = 0; i < 8; i++ ) {
//			if ( x.x8[i] != ifbp->IFB_MICRx[i] ) rc = HCF_ERR_MIC;
//		}
/*14*/  if (x.x32[0] != CNV_LITTLE_TO_LONG(*(hcf_32*)&ifbp->IFB_MICRx[0])) rc = HCF_ERR_MIC;
		if (x.x32[1] != CNV_LITTLE_TO_LONG(*(hcf_32*)&ifbp->IFB_MICRx[4])) rc = HCF_ERR_MIC;
	}
	HCFASSERT( rc == HCF_SUCCESS, rc)
	return rc;
} // check_mic
#endif // HCF_TYPE_WPA / HCF_TYPE_USB
#endif // HCF_DL_ONLY


#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		cmd_cmpl
*.DESCRIPTION	waits for Hermes Command Completion
*
*.ARGUMENTS
*	int cmd_cmpl( IFBP ifbp )
*
*.RETURNS
*	IFB_DefunctStat
*	HCF_ERR_TIME_OUT
*	HCF_ERR_DEFUNCT_CMD_SEQ
*	HCF_SUCCESS
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*
*	Description:
*
*.DIAGRAM
*
*2:	Once cmd_cmpl is called, the Busy option bit in IFB_Cmd must be cleared
*4:	If Status register and command code don't match either:
*	 - the Hermes and Host are out of sync ( a fatal error)
*	 - error bits are reported via the Status Register.
*	Out of sync is considered fatal and brings the HCF in Defunct mode
*	Errors reported via the Status Register should be caused by sequence violations in Hermes command
*	sequences and hence these bugs should have been found during engineering testing. Since there is no
*	strategy to cope with this problem, it might as well be ignored at run time. Note that for any particular
*	situation where a strategy is formulated to handle the consequences of a particular bug causing a
*	particular Error situation reported via the Status Register, the bug should be removed rather than adding
*	logic to cope with the consequences of the bug.
*	There have been HCF versions where an error report via the Status Register even brought the HCF in defunct
*	mode (although it was not yet named like that at that time). This is particular undesirable behavior for a
*	general library.
*	Simply reporting the error (as "interesting") is debatable. There also have been HCF versions with this
*	strategy using the "vague" HCF_FAILURE code.
*	The error is reported via:
*	 - MiscErr tally of the HCF Tally set
*	 - the (informative) fields IFB_ErrCmd and IFB_ErrQualifier
*	 - the assert mechanism
*6:	This is a workaround for (supposedly legacy) H-I code where in some F/W versions the download would
*	do an implicit initialize, thus clearing the Command register.
*8:	Here the Defunct case and the Status error are separately treated
*
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
cmd_cmpl( IFBP ifbp )
{

PROT_CNT_INI
int		rc = HCF_SUCCESS;
hcf_16	stat;

	HCFLOGENTRY( HCF_TRACE_CMD_CPL, ifbp->IFB_Cmd )
	ifbp->IFB_Cmd &= ~HCMD_BUSY;												/* 2 */
	HCF_WAIT_WHILE( (IPW( HREG_EV_STAT) & HREG_EV_CMD) == 0 );					/* 4 */
	stat = IPW( HREG_STAT );
#if HCF_PROT_TIME
	if ( prot_cnt == 0 ) {
		IF_TALLY( ifbp->IFB_HCF_Tallies.MiscErr++; )
		rc = HCF_ERR_TIME_OUT;
		HCFASSERT( DO_ASSERT, ifbp->IFB_Cmd )
	} else
#endif // HCF_PROT_TIME
	{
		DAWA_ACK( HREG_EV_CMD );
/*4*/	if ( stat != (ifbp->IFB_Cmd & HCMD_CMD_CODE) ) {
#if !((HCF_TYPE) & HCF_TYPE_HII)
/*6*/		if ( ifbp->IFB_Cmd != HCMD_PROGRAM )
#endif // HCF_TYPE_HII
			{
/*8*/			if ( ( (stat ^ ifbp->IFB_Cmd ) & HCMD_CMD_CODE) != 0 ) {
					rc = ifbp->IFB_DefunctStat = HCF_ERR_DEFUNCT_CMD_SEQ;
					ifbp->IFB_CardStat |= CARD_STAT_DEFUNCT;
				}
			}
			IF_TALLY( ifbp->IFB_HCF_Tallies.MiscErr++; )
			ifbp->IFB_ErrCmd = stat;
			ifbp->IFB_ErrQualifier = IPW( HREG_RESP_0 );
			HCFASSERT( DO_ASSERT, ifbp->IFB_Cmd | (IPW(HREG_PARAM_0 )) << 16 )
			HCFASSERT( DO_ASSERT, ifbp->IFB_ErrCmd | ifbp->IFB_ErrQualifier << 16 )
		}
	}
	HCFASSERT( rc == HCF_SUCCESS, rc)
	HCFLOGEXIT( HCF_TRACE_CMD_CPL )
	return rc;
} // cmd_cmpl
#endif // HCF_TYPE_USB


#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == ( HCF_TYPE_USB | HCF_TYPE_HII )
// nothing Hii USB
#else
/************************************************************************************************************
*
*.MODULE		cmd_exe
*.DESCRIPTION	Executes synchronous part of Hermes Command and - optionally - waits for Command Completion
*
*.ARGUMENTS
*	int cmd_exe( IFBP ifbp, int cmd_code, int par_0 )
*
*.RETURNS
*	IFB_DefunctStat
*	HCF_ERR_DEFUNCT_CMD_SEQ
*	HCF_SUCCESS
*	HCF_ERR_TO_BE_ADDED	<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	cmd_code
*	par_0
*
*	Description: Executes synchronous Hermes Command and waits for Command Completion
*
*	The general HCF strategy is to wait for command completion. As a consequence:
*	 - the read of the busy bit before writing the command register is superfluous
*	 - the Hermes requirement that no Inquiry command may be executed if there is still an unacknowledged
*	   Inquiry command outstanding, is automatically met.
*	The Tx command uses the "Busy" bit in the cmd_code parameter to deviate from this general HCF strategy.
*	The idea is that by not busy-waiting on completion of this frequently used command the processor
*	utilization is diminished while using the busy-wait on all other seldom used commands the flow is kept
*	simple.
*
*.DIAGRAM
*
*1:	skip the body of cmd_exe when in defunct mode or when - based on the S/W Support register write and
*	read back test - there is apparently no NIC.
*	Note: we gave up on the "old" strategy to write the S/W Support register at magic only when needed. Due to
*	the intricateness of Hermes F/W varieties ( which behave differently as far as corruption of the S/W
*	Support register is involved), the increasing number of Hermes commands which do an implicit initialize
*	(thus modifying the S/W Support register) and the workarounds of some OS/Support S/W induced aspects (e.g.
*	the System Soft library at WinNT which postpones the actual mapping of I/O space up to 30 seconds after
*	giving the go-ahead), the "magic" strategy is now reduced to a simple write and read back. This means that
*	problems like a bug tramping over the memory mapped Hermes registers will no longer be noticed as side
*	effect of the S/W Support register check.
*2:	check whether the preceding command skipped the busy wait and if so, check for command completion
*4:	cope with the behavior of some F/W versions to do an implicit Initialize on commands like Download,
*	Initialize, Program
*
*.NOTICE
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/

int
cmd_exe( IFBP ifbp, hcf_16 cmd_code, int par_0 )	//if HCMD_BUSY of cmd_code set, then do NOT wait for completion
{

int rc;

	HCFLOGENTRY( HCF_TRACE_CMD_EXE, cmd_code )
	HCFASSERT( (cmd_code & HCMD_CMD_CODE) != HCMD_TX || cmd_code & HCMD_BUSY, cmd_code ) //Tx must have Busy bit set
#if (HCF_TYPE) & HCF_TYPE_USB
	rc = put_usb_nic_cmd( ifbp, cmd_code &~HCMD_BUSY, (hcf_16)par_0, 0, 0 );
#else // HCF_TYPE_USB
	OPW( HREG_SW_0, HCF_MAGIC );
	if ( IPW( HREG_SW_0) == HCF_MAGIC ) rc = ifbp->IFB_DefunctStat;						/* 1 */
	else rc = HCF_ERR_NO_NIC;
	if ( rc == HCF_SUCCESS ) {
		//;?is this a hot idea, better MEASURE performance impact
/*2*/	if ( ifbp->IFB_Cmd & HCMD_BUSY ) rc = cmd_cmpl( ifbp );
		OPW( HREG_PARAM_0, par_0 );
		OPW( HREG_CMD, cmd_code &~HCMD_BUSY );
		ifbp->IFB_Cmd = cmd_code;
		if ( (cmd_code & HCMD_BUSY) == 0 ) {	//;?is this a hot idea, better MEASURE performance impact
			rc = cmd_cmpl( ifbp );
/*4*/		OPW( HREG_SW_0, HCF_MAGIC );
		}
	}
#endif // HCF_TYPE_USB
	HCFASSERT( rc == HCF_SUCCESS, cmd_code )
	HCFLOGEXIT( HCF_TRACE_CMD_EXE )
	return rc;
} // cmd_exe

#endif // HCF_TYPE_USB / HCF_TYPE_HII


#if HCF_DLV || HCF_DLNV
#if (HCF_TYPE) & HCF_TYPE_HII
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE			download
*.DESCRIPTION
*
*.ARGUMENTS
*	int download( IFBP ifbp, CFG_PROG_STRCT FAR *ltvp )
*
*.RETURNS
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	ltvp		specifies the pseudo-RID (as defined by WCI)
*
*.DIAGRAM
*1:	First, Ack everything to unblock a (possibly) blocked cmd pipe line
*	Note 1: it is very likely that an Alloc event is pending and very well possible that a (Send) Cmd event is
*	pending
*	Note 2: it is assumed that this strategy takes away the need to ack every conceivable event after an
*	Hermes Initialize
*
*
************************************************************************************************************/
#if HCF_DLNV
not yet supported;
#endif // HCF_DLNV
#if HCF_DLV
int
download( IFBP ifbp, CFG_PROG_STRCT FAR *ltvp )						//Hermes-II download (volatile only)
{

hcf_16				i;
int					rc = HCF_SUCCESS;
//CFG_PROG_STRCT FAR	*p = ltvp;
wci_bufp			cp;
//wci_recordp			cp;			//;?once strio is removed the hcf_8/16* confusion can be removed too
hcf_io				io_port = ifbp->IFB_IOBase + HREG_AUX_DATA;

	HCFLOGENTRY( HCF_TRACE_DL, ltvp->typ )
													//if initial "program" LTV
	if ( ifbp->IFB_DLMode == CFG_PROG_STOP && ltvp->mode == CFG_PROG_VOLATILE) {
													//.  switch Hermes to initial mode
/*1*/	OPW( HREG_EV_ACK, (hcf_16)~HREG_EV_SLEEP_REQ );
		rc = cmd_exe( ifbp, HCMD_INI, 0 );	/* HCMD_INI can not be part of init() because that is called on
											 * other occasions as well */
		rc = init( ifbp );
	}
													//if final "program" LTV
	if ( ltvp->mode == CFG_PROG_STOP && ifbp->IFB_DLMode == CFG_PROG_VOLATILE) {
													//.  start tertiary (or secondary)
		OPW( HREG_PARAM_1, (hcf_16)(ltvp->nic_addr >> 16) );
		rc = cmd_exe( ifbp, HCMD_EXECUTE, (hcf_16)(ltvp->nic_addr & 0xFFFF ) );
		if (rc == HCF_SUCCESS) rc = init( ifbp );	/*;? do we really want to skip init if cmd_exe failed, i.e.
													 *	 IFB_FW_Comp_Id is than possibly incorrect */
													//else (non-final)
	} else {
													//.  if mode == Readback SEEPROM
		if ( 0 /*len is definitely not want we want;?*/ && ltvp->mode == CFG_PROG_SEEPROM_READBACK ) {
			OPW( HREG_PARAM_1, (hcf_16)(ltvp->nic_addr >> 16) );
			OPW( HREG_PARAM_2, MUL_BY_2(ltvp->len - 4));
													//.  .  perform Hermes prog cmd with appropriate mode bits
			rc = cmd_exe( ifbp, HCMD_PROGRAM | ltvp->mode, (hcf_16)(ltvp->nic_addr & 0xFFFF) );
													//.  .  set up NIC RAM addressability according Resp0-1
			OPW( HREG_AUX_PAGE,   IPW( HREG_RESP_1) );
			OPW( HREG_AUX_OFFSET, IPW( HREG_RESP_0) );
													//.  .  set up L-field of LTV according Resp2
			i = ( IPW( HREG_RESP_2 ) + 1 ) / 2;  // i contains max buffer size in words, a probably not very useful piece of information ;?
/*Nico's code based on i is the "real amount of data available"
			if ( ltvp->len - 4 < i ) rc = HCF_ERR_LEN;
			else ltvp->len = i + 4;
*/
/* Rolands code based on the idea that a MSF should not ask for more than is available
			// check if number of bytes requested exceeds max buffer size
			if ( ltvp->len - 4 > i ) {
				rc = HCF_ERR_LEN;
				ltvp->len = i + 4;
			}
*/
													//.  .  copy data from NIC via AUX port to LTV
			cp = (wci_bufp)ltvp->host_addr;						/*IN_PORT_STRING_16 macro may modify its parameters*/
			i = ltvp->len - 4;
			io_port	= ifbp->IFB_IOBase + HREG_AUX_DATA;
			IN_PORT_STRING_16( io_port, cp, i );		//!!!WORD length, cp MUST be a char pointer	// $$ char
													//.  else (non-final programming)
		} else {
													//.  .  get number of words to program
			HCFASSERT( ltvp->segment_size, *(hcf_16*)ltvp->host_addr )
			i = ltvp->segment_size/2;
													//.  .  copy data (words) from LTV via AUX port to NIC
			cp = (wci_bufp)ltvp->host_addr;						//OUT_PORT_STRING_16 macro may modify its parameters
													//.  .  if mode == volatile programming
			if ( ltvp->mode == CFG_PROG_VOLATILE ) {
													//.  .  .  set up NIC RAM addressability via AUX port
				OPW( HREG_AUX_PAGE, (hcf_16)(ltvp->nic_addr >> 16 << 9 | (ltvp->nic_addr & 0xFFFF) >> 7 ) );
				OPW( HREG_AUX_OFFSET, (hcf_16)(ltvp->nic_addr & 0x007E) );
				OUT_PORT_STRING_16( io_port, cp, i );		//!!!WORD length,  cp MUST be a char pointer
			}
		}
	}
	ifbp->IFB_DLMode = ltvp->mode;					//save state in IFB_DLMode
	HCFASSERT( rc == HCF_SUCCESS, rc )
	HCFLOGEXIT( HCF_TRACE_DL )
	return rc;
} // download
#endif // HCF_DLV
#else	// ((HCF_TYPE) & HCF_TYPE_USB)
static int download_ubsh2(IFBP ifbp, CFG_PROG_STRCT FAR *ltvp )
{
int					rc = HCF_SUCCESS;
//CFG_PROG_STRCT	*p = (CFG_PROG_STRCT  *)ltvp;
	//code here for download
	if ( ltvp->typ != CFG_PROG ) {
		rc = HCF_ERR_TIME_OUT;
	} else {		// right code
		// address is 16bit high and 16 bit low address
		// length is in words, so times 2 to get bytes. subtract the structure size
		//   and add 2*2words (structure contains first data word and the length field itself)
//		ULONG ulAddress = ( ((ULONG)p->high_addr) <<16) | p->low_addr;	// create address
//		ULONG ulBytes   = ((p->len *2 ) + 4) -sizeof(CFG_PROG_STRCT);	// bytes
		//PUCHAR pData    = p->dl_data;	// start data array
//		rc = HCF_ERR_TIME_OUT;
//		rc = HCF_SUCCESS;
		switch (ltvp->mode) {
		  case CFG_PROG_VOLATILE:
			if ( ltvp->segment_size != 0 ) {		 // zero is always OK
				rc= ush_com_program( ifbp->IFB_USBIOp,
									 uhs_hermes2_ram_wr,				// type includes direction
									 ltvp->nic_addr,					// new    ulAddress,
									 (USHORT) (ltvp->segment_size/2),   // GUARANTEED ALWAYS even bytes->words    (USHORT)((ulBytes+1)/2),	// length in words
									 (PUSHORT) ltvp->host_addr,			// new ref instead p->dl_data,
									 HCF_USB_PROGRAM_TIMEOUT_MSEC) ;	// data
			}
			break;

		  case CFG_PROG_STOP					: // start program on ulAddress
			// (not yet going to message mode !)
			/*if ( ifbp->IFB_DLMode == CFG_PROG_VOLATILE )*/ {
				rc = uhs_com_action( ifbp->IFB_USBIOp,
									 UHS_COM_ACTION_START,		// action code
									 ltvp->nic_addr,			// ulAddress,		// action data
									 HCF_USB_PROGRAM_TIMEOUT_MSEC);
			}
			break;
		  default:
			break;
		}

		//ifbp->IFB_DLMode = ltvp->mode;					//save state in IFB_DLMode

	}
	HCFASSERT( rc == HCF_SUCCESS, rc)
	return rc;
}


#endif // HCF_TYPE_USB
#endif // HCF_TYPE_HII
#endif // HCF_DLV / HCF_DLNV

#if HCF_DLV || HCF_DLNV
#if !( (HCF_TYPE) & HCF_TYPE_HII)
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		download
*.DESCRIPTION
*
*.ARGUMENTS
*	int download( IFBP ifbp, LTVP ltvp )
*
*.RETURNS
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	ltvp		specifies the pseudo-RID (as defined by WCI)
*
*.DIAGRAM
*19: if HCF_ASSERT is not defined, the else clause absorbs the "return rc;" statement. This is plainly not the
*	intention, hence the semi-colon after the first HCFASSERT, so there will be at least an empty statement
*	after the else.
*
*
*
************************************************************************************************************/
int
download( IFBP ifbp, CFG_PROG_STRCT FAR *ltvp )				//Hermes-I download (volatile and non-volatile)
{
int		rc = HCF_SUCCESS;
hcf_16	i;

	switch (ltvp->mode) {												// function dispatch table
#if HCF_DLV
	  case CFG_PROG_VOLATILE:											// Setup Download RAM
#endif // HCF_DLV
#if HCF_DLNV
	  case CFG_PROG_NON_VOLATILE:										// Setup Download NV-RAM
#endif // HCF_DLNV
		/* this HCFASSERT never catches, the side effects are needed	*/
		HCFASSERT( ( ifbp->IFB_DLTarget[0] = ifbp->IFB_DLTarget[1] = 0 ) == 0, 0 )
						//bring AP back to STA, bring STA back to PRI
												//.  Ack everything to unblock a (possibly) blocked cmd pipe line
												/* Note 1: it is very well possible that an Alloc event and/or
												 * a (Send) Cmd event is pending
												 * Note 2: it is assumed that this strategy takes away the
												 * need to ack every conceivable event after an Hermes
												 * Initialize
												 */
		ifbp->IFB_DLMode = ltvp->mode;
		OPW( HREG_EV_ACK, (hcf_16)~HREG_EV_SLEEP_REQ );
						//let HCMD_INI in init bring both STA and PRI to STA
		if ( ( rc = cmd_exe( ifbp, HCMD_INI|HCMD_INI_0x0100, 0 ) ) != HCF_SUCCESS ) break;
		if ( ( rc = cmd_exe( ifbp, HCMD_INI, 0 ) ) != HCF_SUCCESS ) break;
		if ( ( rc = aux_cntl( ifbp, HREG_CNTL_AUX_ENA_CNTL ) ) != HCF_SUCCESS ) break;
#if HCF_DLV && HCF_DLNV
		if ( ltvp->mode == CFG_PROG_VOLATILE ) {
#endif // HCF_DLV / HCF_DLNV
#if HCF_DLV
			OPW( HREG_PARAM_1, (hcf_16)((ltvp->nic_addr)>>16) );
			rc = cmd_exe( ifbp, HCMD_PROGRAM_ENABLE_VOLATILE | HCMD_PROGRAM, (hcf_16)(ltvp->nic_addr & 0xFFFF) );
#endif // HCF_DLV
#if HCF_DLV && HCF_DLNV
		} else { // ( mode == CFG_PROG_NON_VOLATILE )
#endif // HCF_DLV / HCF_DLNV
#if HCF_DLNV
			ifbp->IFB_DLTarget[0] = (offsetof(IFB_STRCT, IFB_DLLen) - offsetof(IFB_STRCT, IFB_DLTarget))/2;
			ifbp->IFB_DLTarget[1] = CFG_DL_BUF;
			if ( ( rc =  hcf_get_info( ifbp, (LTV_STRCT*)&ifbp->IFB_DLTarget[0] ) ) != HCF_SUCCESS ) break;
			ifbp->IFB_DLPage	= CNV_LITTLE_TO_INT( ifbp->IFB_DLPage );
			ifbp->IFB_DLOffset	= CNV_LITTLE_TO_INT( ifbp->IFB_DLOffset );
			ifbp->IFB_DLLen		= CNV_LITTLE_TO_INT( ifbp->IFB_DLLen );
#endif // HCF_DLNV
#if HCF_DLV && HCF_DLNV
		}
#endif // HCF_DLV / HCF_DLNV
		break;

	  case CFG_PROG_ADDR:												//Download RAM Addressing information
		ifbp->IFB_DLTarget[0] = /*CNV_LITTLE_TO_INT(;?*/(hcf_16)(ltvp->nic_addr & 0xFFFF );
		ifbp->IFB_DLTarget[1] = /*CNV_LITTLE_TO_INT(;?*/(hcf_16)(ltvp->nic_addr >> 16 );
		break;

	  case CFG_PROG_DATA:												//Download RAM Data
#if HCF_DLV && HCF_DLNV
		if ( ifbp->IFB_DLMode == CFG_PROG_VOLATILE ) {
#endif // HCF_DLV / HCF_DLNV
#if HCF_DLV
//;?d:\develop\consolidate\hcf\hcf.c(3711) : warning C4761: integral size mismatch in argument; conversion supplied
		/*but now the API copies to little so compensate at the HCF side till official correction
		  HCF:: - ( sizeof(CFG_PROG_STRCT)/2 - 2 ) + 1*/
			//ltvp->len += - ( sizeof(CFG_PROG_STRCT)/2 - 2 ) + 1;
			//;?monday ltvp->len += 1 - ( (hcf_16)sizeof(CFG_PROG_STRCT)/2 - 2 );
			download_data( ifbp, ltvp, (hcf_16)(ifbp->IFB_DLTarget[0] & 0x007E),
							(hcf_16)((ifbp->IFB_DLTarget[0]>>7) + (ifbp->IFB_DLTarget[1]<<9)) );
#endif // HCF_DLV
#if HCF_DLV && HCF_DLNV
		} else { // ( mode == CFG_PROG_NON_VOLATILE )
#endif // HCF_DLV / HCF_DLNV
#if HCF_DLNV
		/*but now the API copies to little so compensate at the HCF side till official correction
		  HCF:: - ( sizeof(CFG_PROG_STRCT)/2 - 2 ) + 1*/
			//ltvp->len += - ( sizeof(CFG_PROG_STRCT)/2 - 2 ) + 1;
			//;?monday ltvp->len += - ( (hcf_16)sizeof(CFG_PROG_STRCT)/2 - 2 );
			i = ltvp->segment_size;
			if ( i > ifbp->IFB_DLLen ) rc = HCF_ERR_LEN;
			else {
				OPW( HREG_PARAM_2, i );
				OPW( HREG_PARAM_1, ifbp->IFB_DLTarget[1] );	//high order
//																			Target[0] low order
				rc = cmd_exe( ifbp, HCMD_PROGRAM_ENABLE_NON_VOLATILE | HCMD_PROGRAM, ifbp->IFB_DLTarget[0] );
				if ( rc != HCF_SUCCESS ) break;
				download_data( ifbp, ltvp, ifbp->IFB_DLOffset, ifbp->IFB_DLPage );
				rc = cmd_exe( ifbp, HCMD_PROGRAM | HCMD_PROGRAM_NON_VOLATILE, 0 );
			}
#endif // HCF_DLNV
#if HCF_DLV && HCF_DLNV
		}
#endif // HCF_DLV / HCF_DLNV
		break;

	  case CFG_PROG_STOP:													//Cleanup Download
		//if you do this you could possibly assert on it in some cases::  ifbp->IFB_DLMode = ltvp->mode;
		rc = cmd_exe( ifbp, HCMD_PROGRAM | HCMD_PROGRAM_DISABLE, 0 );
		i = (hcf_16)aux_cntl( ifbp, HREG_CNTL_AUX_DIS_CNTL );
		if ( rc == HCF_SUCCESS ) rc = i;
		i = (hcf_16)init( ifbp );	//, INI_COMPLETE );
		if ( rc == HCF_SUCCESS /*&& i != HCF_ERR_INCOMP_STA*/ ) rc = i;
		break;
	  default:
		HCFASSERT( DO_ASSERT, ltvp->mode )
/*19*/	 /*NOP*/;
		//!rc = HCF_SUCCESS;
	}
	HCFASSERT( rc == HCF_SUCCESS, rc )
	HCFLOGEXIT( HCF_TRACE_DL )
	return rc;
} // download

/************************************************************************************************************
*
*.MODULE		download_data
*.DESCRIPTION
*
*.ARGUMENTS
*	void download_data( IFBP ifbp, LTVP ltvp, hcf_16 offset, hcf_16 page )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	ltvp		specifies the pseudo-RID (as defined by WCI)
*	offset
*	page
*
*.DIAGRAM
************************************************************************************************************/
void
download_data( IFBP ifbp, CFG_PROG_STRCT* ltvp, hcf_16 offset, hcf_16 page )
{

hcf_16		len	= ltvp->segment_size;
wci_bufp	cp = ltvp->host_addr;
hcf_io		io_port	= ifbp->IFB_IOBase + HREG_AUX_DATA;
hcf_32		tl;						//to ease the 32 bits calculation

	HCFASSERT( ifbp->IFB_DLTarget[0] | ifbp->IFB_DLTarget[1], ifbp->IFB_DLMode )

	tl = (hcf_32)len;
	tl += ifbp->IFB_DLTarget[0] + ((hcf_32)ifbp->IFB_DLTarget[1] << 16);
	ifbp->IFB_DLTarget[0] = (hcf_16)tl;
	ifbp->IFB_DLTarget[1] = (hcf_16)(tl >> 16);

	len /= 2;
	OPW( HREG_AUX_OFFSET, offset );
	OPW( HREG_AUX_PAGE, page );
	OUT_PORT_STRING_16( io_port, cp, len );		//!!!WORD length,  cp MUST be a char pointer
} // download_data
#endif // HCF_TYPE_USB
#endif // HCF_TYPE_HII
#endif // HCF_DLV / HCF_DLNV

#if ! defined HCF_DL_ONLY
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		get_fid
*.DESCRIPTION	get allocated FID for either transmit or notify
*
*.ARGUMENTS
*	hcf_16 get_fid( IFBP ifbp )
*
*.RETURNS
*	0	no FID available
*	<>0	FID number
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*
*.DIAGRAM
*	The preference is to use a "pending" alloc. If no alloc is pending, then - if available - the "spare" FID
*	is used.
*	If the spare FID is used, IFB_RscInd (representing the spare FID) must be cleared
*	If the pending alloc is used, the alloc event must be acknowledged to the Hermes.
*	In case the spare FID was depleted and the IFB_RscInd has been "faked" as pseudo resource with a 0x0001
*	value by hcf_service_nic, IFB_RscInd has to be "corrected" again to its 0x0000 value.
*
*	Note that due to the Hermes-II H/W problems which are intended to be worked around by DAWA, the Alloc bit
*	in the Event register is no longer a reliable indication of the presence/absence of a FID. The "Clear FID"
*	part of the DAWA logic, together with the choice of the definition of the return information from get_fid,
*	handle this automatically, i.e. without additional code in get_fid.
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
hcf_16
get_fid( IFBP ifbp )
{

hcf_16 fid = 0;
PROT_CNT_INI

	if ( IPW( HREG_EV_STAT) & HREG_EV_ALLOC) {
		fid = IPW( HREG_ALLOC_FID );
		DAWA_ZERO_FID( HREG_ALLOC_FID )
#if ((HCF_TYPE) & ( HCF_TYPE_HII | HCF_TYPE_HII5 ) ) == HCF_TYPE_HII
		HCF_WAIT_WHILE( ( IPW( HREG_EV_STAT ) & HREG_EV_ACK_REG_READY ) == 0 );
		IF_PROT_TIME( HCFASSERT(prot_cnt, IPW( HREG_EV_STAT ) ) )
#endif // HCF_TYPE_HII / HCF_TYPE_HII5
		DAWA_ACK( HREG_EV_ALLOC );			//!!note that HREG_EV_ALLOC is written only once
		if ( ifbp->IFB_RscInd == 1 ) ifbp->IFB_RscInd = 0;
	} else {
		fid = ifbp->IFB_RscInd;
		ifbp->IFB_RscInd = 0;
	}
	return fid;
} // get_fid
#endif // HCF_TYPE_USB
#endif // HCF_DL_ONLY


/************************************************************************************************************
*
*.MODULE		init
*.DESCRIPTION	Handles common initialization aspects (H-I init, calibration, config.mngmt, allocation)
*
*.ARGUMENTS
*	int init( IFBP ifbp )
*
*.RETURNS
*	HCF_ERR_INCOMP_PRI
*	HCF_ERR_INCOMP_FW
*	HCF_ERR_TIME_OUT
*	>>hcf_get_info
*		HCF_ERR_NO_NIC
*		HCF_ERR_LEN
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*
*	init will successively:
*	- in case of a (non-preloaded) H-I, initialize the NIC
*	- calibrate the S/W protection timer against the Hermes Timer
*	- collect HSI, "active" F/W Configuration Management Information
*	- in case active F/W is Primary F/W: collect Primary F/W Configuration Management Information
*	- check HSI and Primary F/W compatibility with the HCF
*	- in case active F/W is Station or AP F/W: check Station or AP F/W compatibility with the HCF
*	- in case active F/W is not Primary F/W: allocate FIDs to be used in transmit/notify process
*
*.DIAGRAM
*2:	drop all error status bits in IFB_CardStat since they are expected to be re-evaluated.
*4:	Ack everything except HREG_EV_SLEEP_REQ. It is very likely that an Alloc event is pending and
*	very well possible that a Send Cmd event is pending. Acking HREG_EV_SLEEP_REQ is handled by hcf_action(
*	HCF_ACT_INT_ON ) !!!
*6:	Due to the different semantics for H-I and H-II Initialize command, init() can not (since init is
*	called after a download) execute the Hermes Initialize command in case of H-II.
*	The HCF_TYPE_PRELOADED caters for the DOS environment where for certain setups, H-I as well as H-II F/W is
*	loaded by a seperate program.
*	the first init (with HCMD_INI) brings both STA and PRI to STA. The second init results in initial state
*	regardless of the original type of F/W running.
*	The return status of the first cmd_exe is ignored. It is assumed that if the first cmd_exe fails, the
*	second cmd_exe fails too.
*10: Calibrate the S/W time-out protection mechanism by calling calibrate(). Note that possible errors
*	in the calibration process are nor reported by init but will show up via the defunct mechanism in
*	subsequent hcf-calls.
*14: usb_check_comp() is called to have the minimal visual clutter for the legacy H-I USB dongle
*	compatibility check.
*16: The following configuration management related information is retrieved from the NIC:
*	 - HSI supplier
*	 - F/W Identity
*	 - F/W supplier
*	 if appropriate:
*	 - PRI Identity
*	 - PRI supplier
*	 appropriate means on H-I: always
*	 and on H-II if F/W supplier reflects a primary (i.e. only after an Hermes Reset or Init
*	 command).
*	 QUESTION ;? !!!!!! should, For each of the above RIDs the Endianess is converted to native Endianess.
*	 Only the return code of the first hcf_get_info is used. All hcf_get_info calls are made, regardless of
*	 the success or failure of the 1st hcf_get_info. The assumptions are:
*	  - if any call fails, they all fail, so remembering the result of the 1st call is adequate
*	  - a failing call will overwrite the L-field with a 0x0000 value, which services both as an
*		error indication for the values cached in the IFB as making mmd_check_comp fail.
*	 In case of H-I, when getting the F/W identity fails, the F/W is assumed to be H-I AP F/W pre-dating
*	 version 9.0 and the F/W Identity and Supplier are faked accordingly.
*	 In case of H-II, the Primary, Station and AP Identity are merged into a single F/W Identity.
*	 The same applies to the Supplier information. As a consequence the PRI information can no longer be
*	 retrieved when a Tertiary runs. To accommodate MSFs and Utilities who depend on PRI information being
*	 available at any time, this information is cached in the IFB. In this cache the generic "F/W" value of
*	 the typ-fields is overwritten with the specific (legacy) "PRI" values. To actually re-route the (legacy)
*	 PRI request via hcf_get_info, the xxxx-table must be set.  In case of H-I, this caching, modifying and
*	 re-routing is not needed because PRI information is always available directly from the NIC. For
*	 consistency the caching fields in the IFB are filled with the PRI information anyway.
*18: mdd_check_comp() is called to check the Supplier Variant and Range of the Host-S/W I/F (HSI) and the
*	Primary Firmware Variant and Range against the Top and Bottom level supported by this HCF.  If either of
*	these tests fails, the CARD_STAT_INCOMP_PRI bit of IFB_CardStat is set
*	Note: There should always be a primary except during production, so this makes the HCF in its current form
*	unsuitable for manufacturing test systems like the FTS. This can be remedied by an adding a test like
*	ifbp->IFB_PRISup.id == COMP_ID_PRI
*20: In case there is Tertiary F/W and this F/W is Station F/W, the Supplier Variant and Range of the Station
*	Firmware function as retrieved from the Hermes is checked against the Top and Bottom level supported by
*	this HCF.
*	Note: the tertiary F/W compatibility checks are superfluous because the DHF has already checked the
*	compatibility of the Tertiary image with the NIC before the tertiary was downloaded.
*	;? so in essence, this check might just as well be removed or turned into an ASSERT/
*28: In case of non-Primary F/W: allocates and acknowledge a (TX or Notify) FID and allocates without
*	acknowledge another (TX or Notify) FID (the so-called 1.5 alloc scheme) with the following steps:
*	- execute the allocate command by calling cmd_exe
*	- wait till either the alloc event or a time-out occurs
*	- regardless whether the alloc event occurs, call get_fid to
*	  - read the FID and save it in IFB_RscInd to be used as "spare FID"
*	  - acknowledge the alloc event
*	  - do another "half" allocate to complete the "1.5 Alloc scheme"
*	  Note that above 3 steps do not harm and thus give the "cheapest" acceptable strategy.
*	  If a time-out occurred, then report time out status (after all), but forget about updating the
*	  MiscErr count, since the error is highly unlikely and will not occur during runtime but in the
*	  initialization phase
*	NOTE;? !!!! the logic to determine whether the running F/W is Primary or not is flawed. It assumes that
*	the 0xF000 contents of the Resp0 register, as created by the Hermes reset and Hermes Initialize command
*	are not modified by any of the subsequent Hermes command (i.e the Access command) but is guaranteed to be
*	changed by the Hermes Exec command. Although both assumptions are apparently currently met (F/W 1.48) this
*	is unspecified behavior. If the F/W changes (or the flow in the HCF implementation is changed) and the 1st
*	assumption breaks, the HCF will do an allocate, causing a time out after reset/initialize. This is
*	probably perceived as an inconvenience.  If the 2nd assumption breaks, no allocate will be executed hence
*	the S/W is not able to transmit. This is likely perceived as a fatal error. The most logical alternative
*	to base it on IFB_FWIdentity.comp_id matching != COMP_ID_FW_PRI IFB, does not work if Configuration
*	Management is not enabled.
*	When the HCF is split in H-I and H-II, the problem can be solved by changing the flow. In case of H-II
*	Allocate moves after the Exec command, for H-I it stays where it is. Note that the problems are caused by
*	different semantics of the Reset for H-I and H-II.
************************************************************************************************************/
int
init( IFBP ifbp )
{

int	rc = HCF_SUCCESS;

	HCFLOGENTRY( HCF_TRACE_INIT, 0 )
	HCFASSERT( ifbp->IFB_Magic == HCF_MAGIC, ifbp->IFB_Magic )
	HCFASSERT_INT

	ifbp->IFB_CardStat = 0;																			/* 2*/
	IF_NOT_USB(	OPW( HREG_EV_ACK, (hcf_16)~HREG_EV_SLEEP_REQ ); )											/* 4*/
#if ( (HCF_TYPE) & (HCF_TYPE_HII | HCF_TYPE_PRELOADED) ) == 0										/* 6*/
	rc = cmd_exe( ifbp, HCMD_INI, 0 );
#endif // HCF_TYPE_HII / HCF_TYPE_PRELOADED
	IF_PROT_TIME( IF_NOT_USB( calibrate( ifbp ); ) )												/*10*/
	ifbp->IFB_FWIdentity.len = sizeof(CFG_FW_IDENTITY_STRCT)/sizeof(hcf_16) - 1;
	ifbp->IFB_FWIdentity.typ = CFG_FW_IDENTITY;
	rc = hcf_get_info( ifbp, (LTVP)&ifbp->IFB_FWIdentity.len );
/* ;? conversion should not be needed for mmd_check_comp */
#if HCF_BIG_ENDIAN
	ifbp->IFB_FWIdentity.comp_id       = CNV_LITTLE_TO_INT( ifbp->IFB_FWIdentity.comp_id );
	ifbp->IFB_FWIdentity.variant       = CNV_LITTLE_TO_INT( ifbp->IFB_FWIdentity.variant );
	ifbp->IFB_FWIdentity.version_major = CNV_LITTLE_TO_INT( ifbp->IFB_FWIdentity.version_major );
	ifbp->IFB_FWIdentity.version_minor = CNV_LITTLE_TO_INT( ifbp->IFB_FWIdentity.version_minor );
#endif // HCF_BIG_ENDIAN
#if defined MSF_COMPONENT_ID																		/*14*/
#if ( (HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == HCF_TYPE_USB
	if ( rc == HCF_SUCCESS ) rc = check_comp_usb( ifbp );
#endif // HCF_TYPE_USB / HCF_TYPE_HII
	if ( rc == HCF_SUCCESS ) {																		/*16*/
		ifbp->IFB_HSISup.len = sizeof(CFG_SUP_RANGE_STRCT)/sizeof(hcf_16) - 1;
		ifbp->IFB_HSISup.typ = CFG_NIC_HSI_SUP_RANGE;
		rc = hcf_get_info( ifbp, (LTVP)&ifbp->IFB_HSISup.len );
/* ;? conversion should not be needed for mmd_check_comp , BUT according to a report of a BE-user it is
 * should be resolved in the WARP release
 * since some compilers make ugly but unnecessary code of these instructions even for LE,
 * it is conditionally compiled */
#if HCF_BIG_ENDIAN
		ifbp->IFB_HSISup.role    = CNV_LITTLE_TO_INT( ifbp->IFB_HSISup.role );
		ifbp->IFB_HSISup.id      = CNV_LITTLE_TO_INT( ifbp->IFB_HSISup.id );
		ifbp->IFB_HSISup.variant = CNV_LITTLE_TO_INT( ifbp->IFB_HSISup.variant );
		ifbp->IFB_HSISup.bottom  = CNV_LITTLE_TO_INT( ifbp->IFB_HSISup.bottom );
		ifbp->IFB_HSISup.top     = CNV_LITTLE_TO_INT( ifbp->IFB_HSISup.top );
#endif // HCF_BIG_ENDIAN
		ifbp->IFB_FWSup.len = sizeof(CFG_SUP_RANGE_STRCT)/sizeof(hcf_16) - 1;
		ifbp->IFB_FWSup.typ = CFG_FW_SUP_RANGE;
		hcf_get_info( ifbp, (LTVP)&ifbp->IFB_FWSup.len );
/* ;? conversion should not be needed for mmd_check_comp */
#if HCF_BIG_ENDIAN
		ifbp->IFB_FWSup.role    = CNV_LITTLE_TO_INT( ifbp->IFB_FWSup.role );
		ifbp->IFB_FWSup.id      = CNV_LITTLE_TO_INT( ifbp->IFB_FWSup.id );
		ifbp->IFB_FWSup.variant = CNV_LITTLE_TO_INT( ifbp->IFB_FWSup.variant );
		ifbp->IFB_FWSup.bottom  = CNV_LITTLE_TO_INT( ifbp->IFB_FWSup.bottom );
		ifbp->IFB_FWSup.top     = CNV_LITTLE_TO_INT( ifbp->IFB_FWSup.top );
#endif // HCF_BIG_ENDIAN

#if (HCF_TYPE) & HCF_TYPE_HII
/* ;? if above conversions are removed since they should not be needed for mmd_check_comp, the CNV_LITTLE_TO_INT on the next line
 *    and pssibly at other places is needed
 */
		if ( /*CNV_LITTLE_TO_INT*/(ifbp->IFB_FWSup.id) == COMP_ID_PRI ) {												/* 20*/
int i = sizeof( CFG_FW_IDENTITY_STRCT) + sizeof(CFG_SUP_RANGE_STRCT );
			while ( i-- ) ((hcf_8*)(&ifbp->IFB_PRIIdentity))[i] = ((hcf_8*)(&ifbp->IFB_FWIdentity))[i];
			ifbp->IFB_PRIIdentity.typ = CFG_PRI_IDENTITY;
			ifbp->IFB_PRISup.typ = CFG_PRI_SUP_RANGE;
			xxxx[xxxx_PRI_IDENTITY_OFFSET] = &ifbp->IFB_PRIIdentity.len;
			xxxx[xxxx_PRI_IDENTITY_OFFSET+1] = &ifbp->IFB_PRISup.len;
		}
#else
		ifbp->IFB_PRIIdentity.len = sizeof(CFG_FW_IDENTITY_STRCT)/sizeof(hcf_16) - 1;
		ifbp->IFB_PRIIdentity.typ = CFG_PRI_IDENTITY;
		hcf_get_info( ifbp, (LTVP)&ifbp->IFB_PRIIdentity.len );
/* ;? conversion should not be needed for mmd_check_comp */
#if HCF_BIG_ENDIAN
		ifbp->IFB_PRIIdentity.comp_id       = CNV_LITTLE_TO_INT( ifbp->IFB_PRIIdentity.comp_id );
		ifbp->IFB_PRIIdentity.variant       = CNV_LITTLE_TO_INT( ifbp->IFB_PRIIdentity.variant );
		ifbp->IFB_PRIIdentity.version_major = CNV_LITTLE_TO_INT( ifbp->IFB_PRIIdentity.version_major );
		ifbp->IFB_PRIIdentity.version_minor = CNV_LITTLE_TO_INT( ifbp->IFB_PRIIdentity.version_minor );
#endif // HCF_BIG_ENDIAN

		ifbp->IFB_PRISup.len = sizeof(CFG_SUP_RANGE_STRCT)/sizeof(hcf_16) - 1;
		ifbp->IFB_PRISup.typ = CFG_PRI_SUP_RANGE;
		hcf_get_info( ifbp, (LTVP)&ifbp->IFB_PRISup.len );
/* ;? conversion should not be needed for mmd_check_comp */
#if HCF_BIG_ENDIAN
		ifbp->IFB_PRISup.role    = CNV_LITTLE_TO_INT( ifbp->IFB_PRISup.role );
		ifbp->IFB_PRISup.id      = CNV_LITTLE_TO_INT( ifbp->IFB_PRISup.id );
		ifbp->IFB_PRISup.variant = CNV_LITTLE_TO_INT( ifbp->IFB_PRISup.variant );
		ifbp->IFB_PRISup.bottom  = CNV_LITTLE_TO_INT( ifbp->IFB_PRISup.bottom );
		ifbp->IFB_PRISup.top     = CNV_LITTLE_TO_INT( ifbp->IFB_PRISup.top );
#endif // HCF_BIG_ENDIAN
#endif // HCF_TYPE_HII
		if ( !mmd_check_comp( (void*)&cfg_drv_act_ranges_hsi, &ifbp->IFB_HSISup)				 /* 22*/
#if ( (HCF_TYPE) & HCF_TYPE_PRELOADED) == 0
//;? the PRI compatibility check is only relevant for DHF
			 || !mmd_check_comp( (void*)&cfg_drv_act_ranges_pri, &ifbp->IFB_PRISup)
#endif // HCF_TYPE_PRELOADED
		   ) {
			ifbp->IFB_CardStat = CARD_STAT_INCOMP_PRI;
			rc = HCF_ERR_INCOMP_PRI;
		}
		if ( ( CNV_LITTLE_TO_INT(ifbp->IFB_FWSup.id) == COMP_ID_STA &&												/* 24 */
			   !mmd_check_comp( (void*)&cfg_drv_act_ranges_sta, &ifbp->IFB_FWSup) ) ||
			 ( CNV_LITTLE_TO_INT(ifbp->IFB_FWSup.id) == COMP_ID_APF &&
			   !mmd_check_comp( (void*)&cfg_drv_act_ranges_apf, &ifbp->IFB_FWSup) )
		   ) {
			ifbp->IFB_CardStat |= CARD_STAT_INCOMP_FW;
			rc = HCF_ERR_INCOMP_FW;
		}

	}
#endif // MSF_COMPONENT_ID
#if !((HCF_TYPE) & HCF_TYPE_USB) && ! defined HCF_DL_ONLY										/* 28 */
	if ( rc == HCF_SUCCESS && ifbp->IFB_FWIdentity.comp_id >= COMP_ID_FW_STA ) {
PROT_CNT_INI
		if ( ( rc = cmd_exe( ifbp, HCMD_ALLOC, 0 ) ) == HCF_SUCCESS ) {
			HCF_WAIT_WHILE( (IPW( HREG_EV_STAT ) & HREG_EV_ALLOC) == 0 );
			IF_PROT_TIME( HCFASSERT(prot_cnt, IPW( HREG_EV_STAT ) ) /*NOP;?*/;)
			ifbp->IFB_RscInd = get_fid( ifbp );
			HCFASSERT( ifbp->IFB_RscInd, 0 )
			cmd_exe( ifbp, HCMD_ALLOC, 0 );
			IF_PROT_TIME( if ( prot_cnt == 0 ) rc = HCF_ERR_TIME_OUT; )
		}
	}
#endif // HCF_TYPE_USB / HCF_DL_ONLY
	HCFASSERT( rc == HCF_SUCCESS, rc )
	HCFLOGEXIT( HCF_TRACE_INIT )
	return rc;
} // init

#if ! defined HCF_DL_ONLY
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		isr_info
*.DESCRIPTION	handles link events
*
*.ARGUMENTS
*	void isr_info( IFBP ifbp )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*
*	Description:
*
*
*.DIAGRAM
*1:	First the FID number corresponding with the InfoEvent is determined.
*	Note the complication of the zero-FID protection sub-scheme in DAWA.
*	Next the L-field and the T-field are fetched into scratch buffer info.
*2:	In case of tallies, the 16 bits Hermes values are accumulated in the IFB into 32 bits values. Info[0]
*	is (expected to be) HCF_NIC_TAL_CNT + 1. The contraption "while ( info[0]-- >1 )" rather than
*	"while ( --info[0] )" is used because it is dangerous to determine the length of the Value field by
*	decrementing info[0]. As a result of a bug in some version of the Hermes, info[0] may be 0, resulting
*	in a very long loop in the pre-decrement logic.
*	In case of the Lucent AccessPoint, the tallies are put in the MailBox rather than in the IFB.
*4:	In case of a link status frame, the information is copied to the IFB field IFB_linkStat
*6:	All other than Tallies (including "unknown" ones and link status which is also put in IFB_LinkStat) are
*	put in the MailBox.  Although put_info_mb is robust against a len-parameter with value zero, it accepts
*	any bogus value for the type-parameter.
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
void
isr_info( IFBP ifbp )
{

hcf_16	info[2], fid;

	HCFTRACE( ifbp, HCF_TRACE_ISR_INFO );																/* 1 */
	fid = IPW( HREG_INFO_FID );
	DAWA_ZERO_FID( HREG_INFO_FID )
#if (HCF_TYPE) & HCF_TYPE_HII
	if ( fid )
#endif // HCF_TYPE_HII
	{
		(void)strio_16(ifbp, IO_IN, fid, 0, info, 2 BE_PAR(2) );
		HCFASSERT( info[0] <= HCF_MAX_LTV, info[0] )
		HCFASSERT( info[0] <= HCF_MAX_LTV, info[1] )
#if HCF_TALLIES & 0x0001		//Hermes tally support
		if ( info[1] == CFG_TALLIES ) {
hcf_32	*p;
/*2*/		if ( info[0] > HCF_NIC_TAL_CNT ) info[0] = HCF_NIC_TAL_CNT + 1;
			p = (hcf_32*)&ifbp->IFB_NIC_Tallies;
			while ( info[0]-- >1 ) *p++ += IPW( HREG_DATA_1 );	//request may return zero length
		}
		else
#endif // HCF_TALLIES
		{
/*4*/		if ( info[1] == CFG_LINK_STAT ) ifbp->IFB_LinkStat = IPW( HREG_DATA_1 );
#if HCF_MB_ON
/*6*/		ifbp->IFB_MB_FID = fid;
			put_info_mb( ifbp, info[1], NULL, info[0] );
#endif // HCF_MB_ON
		}
		HCFTRACE( ifbp, HCF_TRACE_ISR_INFO | HCF_TRACE_EXIT );
	}
	return;
} // isr_info
#endif // HCF_TYPE_USB
#endif // HCF_DL_ONLY


/************************************************************************************************************
*
*.MODULE			hcf_assert
*.DESCRIPTION	filters assert on level and interfaces to the MSF supplied msf_assert routine
*
*.ARGUMENTS
*	void hcf_assert( IFBP ifbp, unsigned int line_number, int q )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp			address of the Interface Block
*	line_number	line number of the line which caused the assert
*	q				qualifier, additional information which may give a clue about the problem
*
*.DIAGRAM
*
*.ENDOC				END DOCUMENTATION
*
*.NOTICE
* mdd_assert has been through a turmoil, renaming hcf_assert to assert and hcf_assert again and supporting off
* and on being called from the MSF level and other ( immature ) ModularDriverDevelopment modules like DHF and
* MMD.
 * !!!! The assert routine is not an hcf_..... routine in the sense that it may be called by the MSF,
 *		however it is called from mmd.c and dhf.c, so it must be external.
 *		To prevent namespace pollution it needs a prefix, to prevent that MSF programmers think that
 *		they are allowed to call the assert logic, the prefix HCF can't be used, so MDD is selected!!!!
 *
* When called from the DHF module the line number is incremented by DHF_FILE_NAME_OFFSET and when called fromn
* the MMD module by MMD_FILE_NAME_OFFSET.
*
************************************************************************************************************/
#if HCF_ASSERT
void
mdd_assert( IFBP ifbp, unsigned int line_number, hcf_32 q )
{

hcf_16	run_time_flag = ifbp->IFB_AssertLvl;

	if ( run_time_flag /* > ;?????? */ ) { //prevent recursive behavior, later to be extended to level filtering
		ifbp->IFB_AssertQualifier = q;
		ifbp->IFB_AssertLine = (hcf_16)line_number;
		if ( ifbp->IFB_AssertRtn ) {
			ifbp->IFB_AssertRtn( line_number, ifbp->IFB_AssertTrace, q );
		}
#if HCF_ASSERT & 0x0002
		OPW( HREG_SW_2, line_number );
		OPW( HREG_SW_2, ifbp->IFB_AssertTrace );
		OPW( HREG_SW_2, (hcf_16)q );
		OPW( HREG_SW_2, (hcf_16)(q >> 16 ) );
#endif // HCF_ASSERT & 0x0002

#if HCF_MB_ON && HCF_ASSERT & 0x0004
		ifbp->IFB_AssertLvl = 0;									// prevent recursive behavior
		put_info_mb( ifbp, CFG_MB_ASSERT, (wci_recordp)&ifbp->IFB_AssertLine, LOF(CFG_MB_ASSERT_STRCT)  );
		ifbp->IFB_AssertLvl = run_time_flag;						// restore appropriate filter level
#endif // !HCF_MB_ON && HCF_ASSERT & 0x0004
	}
} // mdd_assert
#endif // HCF_ASSERT

#if ((HCF_TYPE) & HCF_TYPE_USB ) == 0
/************************************************************************************************************
*
*.MODULE		put_info
*.DESCRIPTION
*
*.ARGUMENTS
*	int put_info( IFBP ifbp, LTVP ltvp )
*
*.RETURNS
*	HCF_SUCCESS
*	>>strio_16
*	>>cmd_wait
*
*.NARRATIVE
*
* Parameters:
*	ifbp		address of the Interface Block
*	ltvp		address in NIC RAM where LVT-records are located
*
*.DIAGRAM
*20: do not write RIDs to NICs which have incompatible Firmware
*24: If the RID does not exist, the L-field is set to zero.
*	Note that some RIDs can not be read, e.g. the pseudo RIDs for direct Hermes commands and CFG_DEFAULT_KEYS
*28: If the RID is written successful, pass it to the NIC by means of an Access Write command
*
*.NOTICE
*	The mechanism to HCF_ASSERT on invalid typ-codes in the LTV record is based on the following strategy:
*	  -	some codes (e.g. CFG_REG_MB) are explicitly handled by the HCF which implies that these codes
*		are valid. These codes are already consumed by hcf_put_info.
*	  - all other codes are passed to the Hermes. Before the put action is executed, hcf_get_info is called
*		with an LTV record with a value of 1 in	the L-field and the intended put action type in the Typ-code
*		field. If the put action type is valid, it is also valid as a get action type code - except
*		for CFG_DEFAULT_KEYS and CFG_ADD_TKIP_DEFAULT_KEY - so the HCF_ASSERT logic of hcf_get_info should
*		not catch.
************************************************************************************************************/
int
put_info( IFBP ifbp, LTVP ltvp	)
{

int rc = HCF_SUCCESS;

	HCFASSERT( (ifbp->IFB_CardStat & (CARD_STAT_INCOMP_PRI|CARD_STAT_INCOMP_FW) ) == 0, ltvp->typ )
	HCFASSERT( CFG_RID_CFG_MIN <= ltvp->typ && ltvp->typ <= CFG_RID_CFG_MAX, ltvp->typ )

	if ( (ifbp->IFB_CardStat & (CARD_STAT_INCOMP_PRI|CARD_STAT_INCOMP_FW) ) == 0 &&			/* 20*/
		 ( (CFG_RID_CFG_MIN <= ltvp->typ && ltvp->typ <= CFG_RID_CFG_MAX ) ||
		 (0xFFE0 <= ltvp->typ && ltvp->typ <= 0xFFFF ) )) {

#if HCF_ASSERT
		if ( ltvp->typ != CFG_DEFAULT_KEYS			&& ltvp->typ != CFG_ADD_TKIP_DEFAULT_KEY	&& \
			 ltvp->typ != CFG_ADD_TKIP_MAPPED_KEY ) {
LTV_STRCT x;
			x.len = 2;
			x.typ = ltvp->typ;
			hcf_get_info( ifbp, (LTVP)&x );
/*24*/		HCFASSERT( x.len, ltvp->typ )
		}
#endif // HCF_ASSERT

		rc = strio_16( ifbp, IO_OUT, ltvp->typ, 0, &ltvp->len, ltvp->len + 1 BE_PAR(2) );
/*28*/	if ( rc == HCF_SUCCESS ) rc = cmd_exe( ifbp, HCMD_ACCESS + HCMD_ACCESS_WRITE, ltvp->typ );
	}
	HCFASSERT( rc == HCF_SUCCESS, rc)
	return rc;
} // put_info

#endif // HCF_TYPE_USB / HCF_TYPE_HII

#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		put_info_mb
*.DESCRIPTION	copies either a PC-RAM located buffer or a RID/FID as a single Info block into the MailBox
*
*.ARGUMENTS
*	void put_info_mb( IFBP ifbp, hcf_16 type, wci_recordp bufp, hcf_16 len )
*	Card Interrupts disabled
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* parameters:
*	ifbp		address of the Interface Block
*	type		in case of a V record in PC RAM:
*				CFG_DIAG, CFG_WMP, CFG_UNK, CFG_DL_STAT or CFG_MB_ASSERT
*				in case of RID/FID:
*				CFG_SCAN, CFG_LINK_STAT, CFG_ASSOC_STAT
*	bufp		pointer to a V record in PC RAM or don't care in case of RID/FID
*	len			length in words of T field + V record in PC RAM
*
*	Description:
*	Note that the definition of len may not be 100% intuitive at first glance, but it is historically grown
*	(in relation ship with the use of put_info_mb to copy LTVs directly from NIC RAM rather than PC RAM) and
*	changing is expected to give more problems than solve them.
*
*	If the data does not fit (including no MailBox is available), the IFB_MBTally is incremented and an
*	HCF_ASSERT catches, but no error status is returned.
*	Calling put_info_mb when their is no MailBox available, is considered a design error in the MSF.
*
*
*	Note that there is always at least 1 word of unused space in the mail box.
*	As a consequence:
*	- no problem in pointer arithmetic (MB_RP == MB_WP means unambiguously mail box is completely empty
*	- There is always free space to write an L field with a value of zero after each MB_Info block.  This
*	  allows for an easy scan mechanism in the "get MB_Info block" logic.
*
*.DIAGRAM
*2:	First the free space in the MailBox is calculated (2a: free part from Write Ptr to Read Ptr, 2b: free part
*	turns out to wrap around) . If this space suffices to store the number of words reflected by len (T-field
*	+ Value-field) plus the additional MailBox Info L-field + a trailing 0 to act as the L-field of a trailing
*	dummy or empty LTV record, then a MailBox Info block is build in the MailBox consisting of
*	  - the value len in the first word
*	  - type in the second word
*	  - a copy of the contents of bufp/RID/FID in the second and higher word
*
*	There is a simple mapping from the publicized WCI I/F for hcf_put_info to put_info_mb.
*	The WMP frame mode is recognized based on the IFB_MB_FID field of the IFB being non-zero.
*
*	In the WMP mode, the MailBox Info block is build by means of input from NIC RAM rather than copied from a
*	buffer. Since the (future) size of WMP frames is not known, it is considered to be an unattractive choice
*	to copy a WMP frame first into a temporary buffer and then use the "standard" put_info_mb to copy the
*	contents of the temporary buffer into the MailBox. Note that there are more arguments for the chosen
*	implementation, e.g. efficiency
*
*4:	Since put_info_mb() can more or less directly be called from the MSF level, the I/F must be robust
*	against out-of-range variables. As failsafe coding, the MB update is skipped by changing tlen to 0 if
*	len == 0; This will indirectly cause an assert as result of the violation of the next if clause.
*6:	Check whether the free space in MailBox suffices (this covers the complete absence of the MailBox).
*	Note that len is unsigned, so even MSF I/F violation works out O.K.
*	The '2' in the expression "len+2" is used because 1 word is needed for L itself and 1 word is needed
*	for the zero-sentinel
*8:	update MailBox Info length report to MSF with "oldest" MB Info Block size
*
*5:	In case of an Hermes Information frame ( CFG_SCAN, CFG_LINK_STAT and CFG_ASSOC_STAT, as reflected by
*	"type >= CFG_INFO_FRAME_MIN"), the L and T are already copied into the MailBox, hence reading the
*	Information frame must start at offset 4.
*	In case of an Hermes Receive frame ( CFG_WMP ), reading must start at offset 0.
* !!!	;? now we are in trouble, if this "if" clause was skipped (because the ReadPointer is larger than the
*	WritePointer), this type dependent code is not executed. Simply copying this code to the 2nd strio does
*	not work either because then it may be executed twice ;?
*
*
*.NOTICE
*	boundary testing depends on the fact that IFB_MBSize is guaranteed to be zero if no MailBox is present,
*	and to a lesser degree, that IFB_MBWp = IFB_MBRp = 0
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
#if HCF_MB_ON
//put_info_mb_sup is a support routine for put_info_mb, however due to the "T"-logic it does not
//necessarily write to the MailBox
void
put_info_mb_sup( IFBP ifbp, hcf_16 offset, wci_recordp sp, wci_recordp dp, hcf_16 len )
{
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
//	if ( sp == NULL )	//;?is this more robust against reentry problem of writing assert to mailbox if mailbox is full
	if ( ifbp->IFB_MB_FID )
		(void)strio_16( ifbp, IO_IN, ifbp->IFB_MB_FID, offset, /*(wci_bufp)*/dp, len BE_PAR(0) );
	else
#endif // HCF_TYPE_USB
		while ( len-- ) *dp++ = *sp++;
} // put_info_mb_sup

/* as discussed with Martin at 20010522: if IFB_MB_FID non-zero AND strio asserts, then you may end
 * up with something like L1T1L2T2V1 in the MB.
 *
 * //hcf_16	run_time_flag = ifbp->IFB_AssertLvl;
 * //		ifbp->IFB_AssertLvl = 0;									// prevent recursive behavior
 */

/* due to the "T"-logic, put_info_mb does not necessarily write to the MailBox, but as a coincidence
 * MsfBuffer also abbreviates to mb
 */
void
put_info_mb( IFBP ifbp, hcf_16 type, wci_recordp sp, hcf_16 len )
{

hcf_16		i;						//byte offset to start writing in MailBox
wci_recordp	dp;						//destination pointer (in MailBox)
hcf_16		tlen;										//free length/working length/offset in WMP frame
/* Offset controls the copying from NIC RAM
 * In case of an LTV, skip LT of LTV, but skip nothing in case of WMP frames
 * not really used for USB
 */
hcf_16		offset = (hcf_16)(type >= CFG_INFO_FRAME_MIN ? 4 : 0);
RID_LOGP	ridp = ifbp->IFB_RIDLogp;					// pointer to (zero-terminated array of) RID_LOG structures

	HCFASSERT( ifbp->IFB_MBp != 0, 0 )					//!!!be careful, don't get into an endless recursion
	HCFASSERT( ifbp->IFB_MBSize, 0 )
#if !((HCF_TYPE) & HCF_TYPE_USB)
//;?	HCFASSERT( ( ifbp->IFB_MB_FID == 0 ) ^ (sp == NULL ) //either IFB_MB_FID or sp (but not both) should be non-zero
//;?			   && !( ifbp->IFB_MB_FID = 0 ) , type )	 //kill the recursion in case bit 0x4 of HCF_ASSERT is set
#endif // HCF_TYPE_USB
//;?	HCFASSERT( sp, type )	this is too simple, see the other sp related assert above

												//reset length and pointers if MailBox empty
//		if ( ( ifbp->IFB_MBInfoLen = *q ) == 0 ) {
//			*ifbp->IFB_MBp = ifbp->IFB_MBRp = ifbp->IFB_MBWp = 0;
//		}

	//;?this excludes tallies from the Tee on non-APs, which seems the easiest way out for the short term
	if ( ridp != NULL ) {
		while ( ridp->typ ) {
			if ( ridp->typ == type ) {
				if ( ( tlen =  min( ridp->len, len ) ) != 0 ) {	//;?might just as well use len iso tlen
					ridp->bufp[0] = tlen--;				//save L, then convert it to number of V-values
					ridp->bufp[1] = type;				//save T
					put_info_mb_sup( ifbp, offset, sp, &ridp->bufp[2], tlen);
				}
/*
 * return;		//;? I like a solution as on the next line but that has some flow impact I want to
							//solve later when the bug is found and corrected
				len = 0;	//data goes either to MSF Buffer or to MailBox
*/
			}
			ridp++;
		}
	}
	if ( ifbp->IFB_MBRp > ifbp->IFB_MBWp ) tlen = ifbp->IFB_MBRp - ifbp->IFB_MBWp;						/* 2a*/
	else {
/* optimization add later after Wrapping algorithm is tested
		if ( ifbp->IFB_MBRp == ifbp->IFB_MBWp ) ifbp->IFB_MBRp = ifbp->IFB_MBWp = 0;
*/
		tlen = ifbp->IFB_MBSize - ifbp->IFB_MBWp;														/* 2b*/
		if ( ( tlen <= len + 2 ) && ( len + 2 < ifbp->IFB_MBRp ) ) {	//if trailing space is too small but
																	//	 leading space is sufficiently large
			ifbp->IFB_MBp[ifbp->IFB_MBWp] = 0xFFFF;					//flag dummy LTV to fill the trailing space
			ifbp->IFB_MBWp = 0;										//reset WritePointer to begin of MailBox
			tlen = ifbp->IFB_MBRp;									//get new available space size
		}
	}
	i = ifbp->IFB_MBWp;
	dp = &ifbp->IFB_MBp[i];
	if ( len == 0 ) tlen = 0; //;? what is this good for
	if ( len + 2 >= tlen ){																				/* 6 */
		//Do Not ASSERT, this is a normal condition
		IF_TALLY( ifbp->IFB_HCF_Tallies.NoBufMB++; ) /*NOP to cover against analomies with empty compound*/;
		HCFASSERT(DO_ASSERT, len)
		HCFASSERT(DO_ASSERT, type )
	} else {
/*8*/	if ( ifbp->IFB_MBInfoLen == 0 )  ifbp->IFB_MBInfoLen = len;
		/* alternative ifbp->IFB_MBInfoLen = ifbp->IFB_MBp[ifbp->IFB_MBRp]; at the end of this routine */
		*dp++ = len--;									/*write Len (= size of T+V in words to MB_Info block
														 *and compensate len for writing Type just below	*/
		*dp++ = type;										/*write Type to MB_Info block						*/

		i += len + 2;										/*update WritePointer of MailBox					*/
		ifbp->IFB_MBWp = i;
		ifbp->IFB_MBp[i] = 0;							//to assure get_info for CFG_MB_INFO stops
		put_info_mb_sup( ifbp, offset, sp, dp, len);
	}
	IF_NOT_USB( ifbp->IFB_MB_FID = 0; )
} // put_info_mb

#endif // HCF_MB_ON
#endif // HCF_DL_ONLY



#if ((HCF_TYPE) & ( HCF_TYPE_USB | HCF_TYPE_HII ) ) == HCF_TYPE_USB
/************************************************************************************************************
*
*.MODULE		put_usb_nic_cmd
*.DESCRIPTION
*
*.ARGUMENTS
*	int put_usb_nic_cmd( IFBP ifbp, hcf_16 h16Cmd, hcf_16 h16Par0, hcf_16 h16Par1, hcf_16 h16Par2)
*
*.RETURNS
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*	h16Cmd
*	h16Par0
*	h16Par1
*	h16Par2
*
*  Description:
*
*
*.DIAGRAM
*
*.NOTICE
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
int
put_usb_nic_cmd( IFBP ifbp, hcf_16 h16Cmd, hcf_16 h16Par0, hcf_16 h16Par1, hcf_16 h16Par2 )
{
CFG_CMD_NIC_STRCT	CCNS;

	if ( h16Cmd == HCMD_DIAG ) h16Par1 = ~ h16Par0;
	if ( h16Cmd == HCMD_INQUIRE ) h16Par1 = 0x3FFF;

	CCNS.len = sizeof( CCNS)/sizeof( hcf_16) - 1;
	CCNS.typ = CFG_CMD_NIC;
	CCNS.cmd = h16Cmd;
	CCNS.parm0 = h16Par0;
	CCNS.parm1 = h16Par1;
	CCNS.parm2 = h16Par2;
	//return put_info( ifbp, (LTVP)&CCNS );
	return UNUSendHcfReq( CCNS.typ, (PUSHORT)&CCNS.cmd, (USHORT)(CCNS.len - 1), NULL);
} // put_usb_nic_cmd
#endif // HCF_TYPE_USB / HCF_TYPE_HII

#if ! defined HCF_DL_ONLY
#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		rcv_msg_frag
*.DESCRIPTION
*
*.ARGUMENTS
*	void rcv_msg_frag( IFBP ifbp, wci_bufp bufp, int len )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*	bufp	address of buffer
*	len	length in bytes of buffer specified by bufp
*
*	Description: process the extraneous bytes (if applicable) read by the previous rcv_msg_frag together with
*	the bytes read from the RxFS by the current rcv_msg_frag by passing them in groups of 4 to the MIC
*	calculation.
*	Only the bytes read by the current rcv_msg_frag are stored in bufp.
*
*.DIAGRAM
*2:	process remainder of previous rcv_msg_frag, i.e. if the previous rcv_msg_frag consumed only 1, 2 or 3
*	bytes of the n times 4 bytes which were read, the complete group of 4 bytes is copied to bufp and the
*	final group of 4 bytes must still be passed to the MIC calculation engine.
*	This last group of 4 bytes is saved in the accumulator IFB_rx_32.
*	If and only if IFB_rx_32 is fully consumed by this rcv_msg_frag, IFB_rx_32 is passed to the MIC
*	Calculation engine.
*	Note that this code must cope with zero-length fragments and non-zero fragments which are nevertheless too
*	small to completely consume the contents of IFB_rx_32. This is achieved by the check on len in the while
*	loop and the test on IFB_rx_tlen == 4 in the if.
*	Note that the MIC calculation over the contents of IFB_rx_32 can NOT be done when these bytes are read
*	from the NIC in the while loop in point 6, because these bytes MAY be the first byte(s) of the MIC
*	already.
*6:	The remaining space in bufp is filled by reading in groups of 2 times 2 bytes from the RxFS in NIC
*	memory. This reading stops at the last group of 4 bytes which fits at least partly in bufp.
*	Each group which fits completely in bufp is copied to bufp and passed to the MIC Calculation engine.
*10: If the last 4 byte group did not fit in bufp, there is still space for one or more (at most 3) bytes
*	in bufp. These bytes are copied from IFB_rx_32.
*	This last group of 4 bytes is completely saved in the accumulator IFB_rx_32.
*
*.NOTICE
*	It turns out DOS ODI uses zero length fragments. The HCF code can cope with it, but as a consequence, no
*	Assert on len is possible
*
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
void
rcv_msg_frag( IFBP ifbp, wci_bufp bufp, int len )
{

union { hcf_32 x32; hcf_16 x16[2]; hcf_8 x8[4]; } x;

	if ( ifbp->IFB_rx_tlen ) {																				/* 2 */
		while ( ifbp->IFB_rx_tlen < 4 && len ) {
			*bufp++ = ifbp->IFB_rx_32[ifbp->IFB_rx_tlen++];
			len--;	//don't be clever and don't move this -- into the while
		}
		if ( ifbp->IFB_rx_tlen == 4 ) {		//cope with extremely small fragments
#if (HCF_TYPE) & HCF_TYPE_WPA		//this if can probably be deleted once MIC_RX_RTN is changed
			MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, *(hcf_32*)ifbp->IFB_rx_32 );
#endif // HCF_TYPE_WPA
			ifbp->IFB_rx_tlen = 0;
		}
	}
	while ( len ) {																					/* 6 */
#if support_32bits
		x.x32 = IN_PORT_HCF32( ifbp->IFB_IOBase + HREG_DATA_1 );
#else
		x.x16[0] = IPW( HREG_DATA_1 );
		x.x16[1]= IPW( HREG_DATA_1 );
#endif // support_32bits
		if ( len < 4 ) break;
#if HCF_ALIGN == 1
		*(hcf_16 FAR*)bufp = CNV_LITTLE_TO_INT(x.x16[0]);
		*(hcf_16 FAR*)(bufp+2)= CNV_LITTLE_TO_INT(x.x16[1]);
#else
		bufp[0 + HCF_BIG_ENDIAN] = x.x8[0];
		bufp[1 - HCF_BIG_ENDIAN] = x.x8[1];
		bufp[2 + HCF_BIG_ENDIAN] = x.x8[2];
		bufp[3 - HCF_BIG_ENDIAN] = x.x8[3];
#endif // HCF_ALIGN
#if (HCF_TYPE) & HCF_TYPE_WPA		//this if can probably be deleted once MIC_RX_RTN is changed
		MIC_RX_RTN( (hcf_32*)ifbp->IFB_MICRx, x.x32 );
#endif // HCF_TYPE_WPA
		bufp += 4;
		len -= 4;
	}
	if ( len ) {																					/* 10*/
		*(hcf_16 FAR *)&ifbp->IFB_rx_32[0] = CNV_LITTLE_TO_INT(x.x16[0]);
		*(hcf_16 FAR *)&ifbp->IFB_rx_32[2] = CNV_LITTLE_TO_INT(x.x16[1]);
		//! ifbp->IFB_rx_tlen = 0;
		while ( len-- ) *bufp++ = ifbp->IFB_rx_32[ifbp->IFB_rx_tlen++];
	}
} // rcv_msg_frag
#endif // HCF_TYPE_USB
#endif // HCF_DL_ONLY

#if ! defined HCF_DL_ONLY
/************************************************************************************************************
*
*.MODULE		send_msg_frag
*.DESCRIPTION
*
*.ARGUMENTS
*	void send_msg_frag( IFBP ifbp, wci_bufp bufp, int len )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*	bufp	address of buffer
*	len	length in bytes of buffer specified by bufp
*
*	Description: process the last bytes (if applicable) of the previous send_msg_frag and the bytes of the
*	current send_msg_frag by passing them in groups of 4 to the MIC calculation, and output them in groups of
*	2 to the TxFS on the NIC
*
*.DIAGRAM
*2:	process remainder of previous send_msg_frag, i.e. if any bytes of the previous send_msg_frag are still
*	in the accumulator IFB_tx_32, waiting for some more bytes, to go as an quartet to the MIC calculation
*	engine, then as many bytes as needed to form this 4-some are copied from the message fragment to
*	IFB_tx_32.
*	Note that this code must cope with zero-length fragments and non-zero fragments which are nevertheless too
*	small to extend the contents of IFB_tx_32 to a quad byte. This is achieved by the check on len in the
*	while loop and the test on IFB_tx_tlen == 4 in the if.
*	In addition to the MIC Calculation, the contents of IFB_tx_32 is written as 2 words to the NIC.
*6:	The remaining bytes are processed in groups of 4 by the MIC Calculation engine. Any group of 4 bytes
*	is written as 2 words to the NIC
*10: If there are one or more (at most 3) bytes left, these bytes are stored in IFB_tx_32 to be processed
*	with the next send_msg_frag or send_mic_dcp.
*
*.NOTICE
*	It turns out DOS ODI uses zero length fragments. The HCF code can cope with it, but as a consequence, no
*	Assert on len is possible
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
#if 1
void
send_msg_frag( IFBP ifbp, wci_bufp bufp, int len )
{

union { hcf_32 x32; hcf_16 x16[2]; hcf_8 x8[4]; } x;

	HCFASSERT( bufp, len )
	HCFASSERT( 0 <= len && len <= HCF_MAX_MSG, len ) //ODI use zero-lenght fragments

	if ( ifbp->IFB_tx_tlen ) {																				/* 2 */
		while ( ifbp->IFB_tx_tlen < 4 && len ) {
			ifbp->IFB_tx_32[ifbp->IFB_tx_tlen++] = *bufp++;
			len--;
		}
		if ( ifbp->IFB_tx_tlen == 4 ) {
			ifbp->IFB_tx_tlen = 0;
#if (HCF_TYPE) & HCF_TYPE_WPA
#if HCF_BIG_ENDIAN
#if HCF_ALIGN == 1				//this saves 16 bytes code
			x.x16[0] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)ifbp->IFB_tx_32);
			x.x16[1] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)(ifbp->IFB_tx_32+2));
#else
			x.x8[0] = ifbp->IFB_tx_32[0 + HCF_BIG_ENDIAN];
			x.x8[1] = ifbp->IFB_tx_32[1 - HCF_BIG_ENDIAN];
			x.x8[2] = ifbp->IFB_tx_32[2 + HCF_BIG_ENDIAN];
			x.x8[3] = ifbp->IFB_tx_32[3 - HCF_BIG_ENDIAN];
#endif // HCF_ALIGN
//JS: use swapped value in union above
			ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, x.x32 );
#else			
			ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, *(hcf_32 BASED *)ifbp->IFB_tx_32 );
#endif // HCF_BIG_ENDIAN
#endif // HCF_TYPE_WPA
			x.x16[0] = CNV_INT_TO_LITTLE(*(hcf_16 FAR *)&ifbp->IFB_tx_32[0] );
			OPW( HREG_DATA_1, x.x16[0] );
			x.x16[1] = CNV_INT_TO_LITTLE(*(hcf_16 FAR *)&ifbp->IFB_tx_32[2] );
			OPW( HREG_DATA_1, x.x16[1] );
		}
	}
	while ( len >= 4 ) {																			/* 6 */
#if HCF_ALIGN == 1				//this saves 16 bytes code
		x.x16[0] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)bufp);
		x.x16[1] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)(bufp+2));
#else
		x.x8[0] = bufp[0 + HCF_BIG_ENDIAN];
		x.x8[1] = bufp[1 - HCF_BIG_ENDIAN];
		x.x8[2] = bufp[2 + HCF_BIG_ENDIAN];
		x.x8[3] = bufp[3 - HCF_BIG_ENDIAN];
#endif // HCF_ALIGN
#if support_32bits
		OUT_PORT_HCF32( ifbp->IFB_IOBase + HREG_DATA_1, x.x32 );
#else
		OPW( HREG_DATA_1, x.x16[0] );
		OPW( HREG_DATA_1, x.x16[1] );
#endif //support_32bits
#if (HCF_TYPE) & HCF_TYPE_WPA
		ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, x.x32 );
#endif // HCF_TYPE_WPA
		bufp += 4;
		len -= 4;
	}
//	if ( len ) {
/*10*/	while ( len-- ) ifbp->IFB_tx_32[ifbp->IFB_tx_tlen++] = *bufp++;
//	}
} // send_msg_frag
#else
/*	this SEEMS a bit / a lot shorter and - depending on your taste - more elegant.
	However, it seems to make a difference of less than 20 bytes in code size.
	It does not function as is, except for multiple of 4 bytes.
	It is difficult to "see" whether it is correct with all weird boundary conditions caused by
	multiple fragments, including zero and sub-4 lengths
*/
void
send_msg_frag( IFBP ifbp, wci_bufp bufp, int len )
{

int			i = 0;
wci_bufp	p = bufp;
union { hcf_32 x32; hcf_16 x16[2]; hcf_8 x8[4]; } x;

	HCFASSERT( bufp, len )

	if ( ifbp->IFB_tx_tlen ) {		//process remainder of previous send_msg_frag
		while ( ifbp->IFB_tx_tlen < 4 && len ) {
			ifbp->IFB_tx_32[ifbp->IFB_tx_tlen++] = *bufp++;
			len--;
		}
		if ( ifbp->IFB_tx_tlen == 4 ) {		//cope with extremely small fragments
			ifbp->IFB_tx_tlen = 0;
			p = ifbp->IFB_tx_32;
			i = -4;
	}
	}
	while ( i < len  ) {
#if HCF_ALIGN == 1
		x.x16[0] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)p);
		x.x16[1] = CNV_INT_TO_LITTLE(*(hcf_16 FAR*)(p+2));
#else
		x.x8[0] = p[0 + HCF_BIG_ENDIAN];
		x.x8[1] = p[1 - HCF_BIG_ENDIAN];
		x.x8[2] = p[2 + HCF_BIG_ENDIAN];
		x.x8[3] = p[3 - HCF_BIG_ENDIAN];
#endif // HCF_ALIGN
		OPW( HREG_DATA_1, x.x16[0] );
		OPW( HREG_DATA_1, x.x16[1] );
#if (HCF_TYPE) & HCF_TYPE_WPA
		ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, x.x32 );
#endif // HCF_TYPE_WPA
		i += 4;
		p = bufp + i;
	}
		while ( len-- ) ifbp->IFB_tx_32[ifbp->IFB_tx_tlen++] = *p++;
} // send_msg_frag
#endif //0
#endif // HCF_DL_ONLY


/************************************************************************************************************
*
*.MODULE		send_mic_dcp
*.DESCRIPTION 	finalize the MIC calculation with the padding pattern, output the last bytes (if applicable)
*				of the message, outputs the MIC and the DCWA/DCP pattern (if applicable)
*
*.ARGUMENTS
*	void send_mic_dcp( IFBP ifbp )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	ifbp	address of the Interface Block
*
*.DIAGRAM
*2:	0 - 3 bytes of the last send_msg_frag are still in IFB_tx_32, so at least 1 and most 4 bytes of the
*	minimum padding of 0x5A000000 can be written to IFB_tx_32.
*4:	If SSN is not compiled in or not activated, the number of bytes left over by the last send_msg_frag is
*	converted in a roud-up fashion to the number of words and these bytes are outputted in point 10
*6:	If SSN is compiled in and activated, the 1st call to the MIC calculation routine feeds the bytes left 
*	over by the last send_msg_frag (if any) and the first (4-1) bytes of the 0x5A,0x00,.... padding to the MIC
*	calculation engine.  
*8:	The 2nd call to the MIC calculation routine feeds 4 0x00 bytes of padding to the MIC calculation
*	engine, thus completing the required 8 to 5 bytes padding.
*10: process remainder -if any- of last send_msg_frag. Note that this remainder in IFB_tx_32 is undisturbed by
*	point 2 - 8. Depending on the number of bytes (1-3) of the last send_msg_frag, 3 - 1 bytes of the MIC are
*	copied from IFB_MICTx to IFB_tx_32 and then IFB_tx_32 is written as 2 words to NIC RAM.
*12: write the remainder of the MIC and possible some garbage to NIC RAM
*	Note: i is always 4 (a loop-invariant of the while in point 2)
*14: here a number of cases are covered
*	a:	SSN is not compiled in or not activated::
*		i reflects the number of words (0-2, see point 2) in IFB_tx_32 leftover by the last send_msg_frag
*		bufp points at IFB_tx_32
*	b:	SSN is compiled in and activated but no bytes in IFB_tx_32 leftover by the last send_msg_frag ::
*		point 10 was skipped
*		bufp points at the first byte of IFB_MICTx
*		i == 4, the number of words in IFB_MICTx
*	c:	SSN is compiled in and activated and 1-3 bytes in IFB_tx_32 are leftover by the last send_msg_frag ::
*		point 10 was executed and wrote 1-3 bytes of the last send_msg_frag and 3-1 bytes of MIC
*		bufp points at the fourth, third or second byte of IFB_MICTx
*		i == 4 or 3, the number of words in IFB_MICTx, corresponding with the not yet outputted part of the
*		MIC.
*		4 words are written if 3 bytes were leftover by the last send_msg_frag. In this case only 1 MIC
*		byte was processed in point 10 leaving 7 MIC bytes to be processed here, after rounding up this makes
*		4 words
*		3 words are written if 1 or 2 bytes were leftover by the last send_msg_frag. In this case 3 or 2 MIC
*		bytes were processed in point 10 leaving 5 or 6 MIC bytes to be processed here, after rounding up this
*		makes 3 words
*16: Write the DCWA/DCP pattern. In case of SSN, the F/W checks the pattern. In case of pre-SSN, the remainder
*	of the DCWA logic is contained in hcf_send_msg
*
*.NOTICE
*
*	DataCorruptionWorkAround scheme (ref:	DCWA W2DN176B1 "another layer of workaround")
* The problem is that without known cause, the Hermes-I internal NIC RAM pointer misses its auto-increment
* causing two successive writes to NIC RAM to address the same location. As a consequence word <n> is
* overwritten and word <n+j> is written at position <n+j-1>. Since the Hermes is unaware of this, nothing in
* the system is going to catch this, e.g. the frame is received on the other side with correct FCS. As a
* workaround, the HCF writes a number of words - the DCWA/DCP (a.k.a kludge pattern) - after the MSF-data.
* Then "something" checks the first word after the (supposed) MSF_data. This first word must match the first
* word of the kludge pattern, otherwise, apparently, the auto-increment failed. We write more than 1 word
* otherwise if the previous use of that piece of NIC RAM would have left by chance the right "kludge" value
* just after the newly but incorrectly put data, the test would not trigger. By overwriting the next 3 words
* as well, we assume the likelihood of this to be sufficiently small.
* The frequency observed of this problem varies from 1 out of 1000 frames till too low to be noticeable. Just
* to be on the safe side we assume that the chance of an error is 10E-3. By writing 4 words we reduce the
* likelihood of an unnoticed problem to 10E-12, but of course this becomes tricky because we don't know
* whether the chances are independent.
* Doing the checking in the HCF, requires the HCF to keep track of where the internal NIC RAM pointer is
* supposed to be, set the BAP register to the location which should contain the kludge pattern and read the
* 1st word back. The required BAP setup is expensive in time (up to 400 microseconds) and the actual time
* needed is not very predictable.
* Doing the checking in the F/W has no impact on the Host processor utilization and is assumed to have a low
* and very predictable impact on the NIC performance.
* Since the stability of the DCWA mechanism is now field-proven, the actual check is moved to the F/W domain.
* This implementation is called DCP (Data Corruption Protection). To accommodate pre-SSN F/W, the DCWA checking
* in the Host is still supported for pre-SSN F/W.
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
void
send_mic_dcp( IFBP ifbp )
{
int		i = ifbp->IFB_tx_tlen;																		/* 2 */
hcf_8	*bufp;

#if (HCF_TYPE) & HCF_TYPE_WPA
	if ( ifbp->IFB_MICKey[0] == 0 ) {
#endif // HCF_TYPE_WPA
		bufp = (hcf_8*)ifbp->IFB_tx_32;																/* 4 */
		i = ( i + 1 ) >> 1;
#if (HCF_TYPE) & HCF_TYPE_WPA
	} else {																						/* 6 */
		ifbp->IFB_tx_32[i] = 0x5A;	
		while ( ++i < 4 ) ifbp->IFB_tx_32[i] = 0x00;
#if HCF_BIG_ENDIAN
		ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx,
							(ifbp->IFB_tx_32[0] << 16)+
							(ifbp->IFB_tx_32[1] << 24) +
							(ifbp->IFB_tx_32[2]      ) +
							(ifbp->IFB_tx_32[3] <<  8)
						  );
#else
		ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, *(hcf_32 BASED *)ifbp->IFB_tx_32 );
#endif // HCF_BIG_ENDIAN
		ifbp->IFB_MICTxRtn( (hcf_32*)ifbp->IFB_MICTx, 0x00000000L );								/* 8 */
		*(hcf_32 *)(&ifbp->IFB_MICTx[0]) = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICTx[0]);
		*(hcf_32 *)(&ifbp->IFB_MICTx[4]) = CNV_LONG_TO_LITTLE(*(hcf_32*)&ifbp->IFB_MICTx[4]);
		bufp = ifbp->IFB_MICTx;
		if ( ifbp->IFB_tx_tlen ) {																	/*10 */
	hcf_16	tmpvar;
			if ( ifbp->IFB_tx_tlen < 3 ) i--;	//prevent output of a garbage word, see point 14
			while ( ifbp->IFB_tx_tlen < 4 ) {
				ifbp->IFB_tx_32[ifbp->IFB_tx_tlen++] = *bufp++;
			}
			tmpvar = CNV_INT_TO_LITTLE(*(hcf_16 FAR *)&ifbp->IFB_tx_32[0] );
			OPW( HREG_DATA_1, tmpvar );
			tmpvar = CNV_INT_TO_LITTLE(*(hcf_16 FAR *)&ifbp->IFB_tx_32[2] );
			OPW( HREG_DATA_1, tmpvar );
		}
	}
#endif // HCF_TYPE_WPA
	while ( i-- ) {																					/*14 */
		OPW( HREG_DATA_1, *bufp | *(bufp+1)<<8 );	//;?change this, use hulpvariable, OPW may have side effects
		bufp += 2;
	}
#if  !( (HCF_TYPE) & HCF_TYPE_HII )																	/*16 */
	OPW( HREG_DATA_1, 0xCAFE );
	OPW( HREG_DATA_1, 0 );
	OPW( HREG_DATA_1, 0 );
	OPW( HREG_DATA_1, 0 );
#endif // HCF_TYPE_HII
} // send_mic_dcp


#if ! ((HCF_TYPE) & HCF_TYPE_USB)
/************************************************************************************************************
*
*.MODULE		strio
*.DESCRIPTION	set up data access to NIC RAM via BAP_1
*				read/write string specified number of words from/to WaveLAN NIC RAM to/from PC RAM
*				optionally converting the Endianess of a part of the words
*
* int strio( IFBP ifbp, hcf_16 type, hcf_16 fid, hcf_16 offset, wci_recordp pc_addr, int tot_len
*		   BE_PAR(int word_len) )
*
*.ARGUMENTS
*	IFBP			ifbp			address of I/F Block
*	int			type            action code
*								  -	IO_IN			read from NIC RAM
*								  -	IO_OUT			write to NIC RAM
*	int			fid				FID/RID
*	int			offset			offset in FID/RID
*	wci_recordp	pc_addr			begin address in PC RAM to write to or read from
*	int			tot_len			number of WORDS (USED TO BE bytes) to write or read
*	int			word_len		This parameter is only present when HCF_BIG_ENDIAN is true
*								number of leading words of which the Endianess must be converted
*								implies offset and pc_addr are even
*
*.RETURNS
*	HCF_SUCCESS					O.K
*	HCF_ERR_NO_NIC				card is removed
*	HCF_ERR_DEFUNCT_TIME_OUT	Fatal malfunction detected
*	HCF_ERR_DEFUNCT_.....		if and only if IFB_DefunctStat <> 0
*
*.NARRATIVE
*
*	strio has the following tasks:
*	- set up data access to NIC RAM via BAP_1
*	- copy data from NIC RAM to Host RAM or vice versa
*	- optionally convert (part of) the data from/to Little Endian format (as used by the NIC) to/from the
*	  Native Endian format (as used by the Host)
*
*	Data is a string with specified word-length copied from/to a specified offset in a specified Receive Frame
*	Structure (FID), Transmit Frame Structure (FID) or Record (RID) to/from a Host RAM buffer with a specified
*	begin address.
*
*	A length of 0 can be specified, resulting in BAP setup but no data transfer. This feature played a role in
*	the optimization when 2 BAPs where used by the HCF. These days this feature services only as a robustness
*	aspect.
*
*	A non-zero return status indicates:
*	- the NIC is considered nonoperational, e.g. due to a time-out of some Hermes activity in the past
*	- BAP_1 could not properly be initialized
*	- the card is removed before completion of the data transfer
*	In all other cases, a zero is returned.
*	If a card removal is returned, the MSF has the option to drop the message or recover in any other way it
*	sees fit.
*	BAP Initialization failure indicates an H/W error which is very likely to signal complete H/W failure.
*	Once a BAP Initialization failure has occurred all subsequent interactions with the Hermes will return a
*	"defunct" status till the Hermes is re-initialized by means of an hcf_connect.
*
*	Strio prepares access via BAP-mechanism to a FID or a RID. A FID or RID is the handle supplied by the
*	Hermes to the Host to access the buffer structures located in NIC RAM.
*	A BAP is a set of registers (Offset, Select and Data) offering read/write access to a particular FID or
*	RID. This access is based on a auto-increment feature.
*	There are two BAPs but these days the HCF uses only BAP_1 and leaves BAP_0 to the PCI Busmastering H/W.
*
*	The BAP-mechanism is based on the Busy bit in the Offset register (see the Hermes definition). The waiting
*	for Busy must occur between writing the Offset register and accessing the Data register. The
*	implementation to wait for the Busy bit drop after each write to the Offset register, implies that the
*	requirement that the Busy bit is low  before the Select register is written, is automatically met.
*	The wait for Busy bit drop is protected by a loop counter, which is initialized with IFB_TickIni, which is
*	calibrated in init.
*
*	BAP-setup may be time consuming (e.g. 380 usec for large offsets occurs frequently) In the past a
*	complicated scheme with S/W "shadows" of BAP0/1 Select/Offset was used to counteract these long times. By
*	restructuring the DCWA and the buffer fragmentation logic, the profit of this shadowing diminished so it
*	was deleted.
*
*	The NIC I/F is optimized for word transfer and can only handle word transfer at a word boundary in NIC
*	RAM. The intended solution for transfer of a single byte has multiple H/W flaws. There have been two
*	different S/W Workaround strategies, one based on NIC RAM, and one based on temporary buffering of odd
*	chars in the IFB. Due to the word oriented nature of the MIC feature, this byte logic became obsolete.
*
*.DIAGRAM
*
*2:	the test on rc checks whether the HCF went into "defunct" mode ( e.g. BAP initialization or a call to
*	cmd_wait did ever fail).
*4:	the select register and offset register are set
*	the offset register is monitored till a successful condition (no busy bit) is detected or till the
*	(calibrated) protection counter expires
*	If the counter expires, this is reflected in IFB_DefunctStat, so all subsequent calls to strio fail
*	immediately ( see 2)
*6:	HREG_OFFSET_ERR is ignored as error because:
*	 a: the Hermes is robust against it
*	 b: it is not known what causes it (probably a bug), hence no strategy can be specified which level is
*		to handle this error in which way. In the past, it could be induced by the MSF level, e.g. by calling
*		hcf_rcv_msg while there was no Rx-FID available. Since this is an MSF-error which is caught by ASSERT,
*		there is no run-time action required by the HCF.
*	Lumping the Offset error in with the Busy bit error, as has been done in the past turns out to be a
*	disaster or a life saver, just depending on what the cause of the error is. Since no prediction can be
*	done about the future, it is "felt" to be the best strategy to ignore this error. One day the code was
*	accompanied by the following comment:
*	//	ignore HREG_OFFSET_ERR, someone, supposedly the MSF programmer ;) made a bug. Since we don't know
*	//	what is going on, we might as well go on - under management pressure - by ignoring it
*8:	The optional conversion of the first words from Native Endian to Little Endian format is done. This
*	implies that Endian conversion can ONLY take place at word boundaries.
*10: The PCMCIA card can be removed in the middle of the transfer. By depositing a "magic number" in the
*	HREG_SW_0 register of the Hermes at initialization time and by verifying this register, it can be
*	determined whether the card is still present. The return status is set accordingly.
*	A (relative) cheap way to prevent that failing I/O results in run-away behavior because garbage on the
*	stack used as a local variable passed to strio is interpreted by the caller of strio irrespective of the
*	return status van strio (e.g. hcf_service_nic has this behavior).
*
*.NOTICE
*	Depending on the selected optimization options, using "register int reg" causes some obscure optimization
*	with si. As a result the code is longer. Therefore, do not invest time to optimize this code that way
*	during a "maintenance cycle".
*
*.NOTICE
*	The IN_/OUT_PORT_WORD_/STRING macros are MSF-programmer defined and may or may not have side effects
*	on their parameters, therefore they can not handle expressions like "len/2". As a solution all these
*	macros must be called via "simple" variables, with no side effects like ++ and their contents is
*	unpredictable at completion of the macro.
*
*.NOTICE
*	When, in a Big Endian environment, hcf_get_info is called for a non-existing RID, strio will be called
*	with a request to read 0 bytes (being the Hermes reported RID length) and 1 word to be converted to Little
*	Endian (being the assumed type). This cause ASSERT to trigger, but that is considered an acceptable
*	nuisance.
*
*.ENDOC                          END DOCUMENTATION
*
************************************************************************************************************/
int
strio_16( IFBP ifbp, hcf_16 type, hcf_16 fid, hcf_16 offset, wci_recordp pc_addr, int tot_len
		  BE_PAR(int word_len) )
{
hcf_io		io_port = ifbp->IFB_IOBase + HREG_DATA_1;	//BAP data register
PROT_CNT_INI
int			rc;
wci_bufp	cp;
int			i;

	HCFTRACE( ifbp, HCF_TRACE_STRIO );
	HCFTRACEVALUE( ifbp, fid );
	HCFTRACEVALUE( ifbp, offset );
	HCFASSERT( ((long)pc_addr & (HCF_ALIGN-1) ) == 0, (hcf_16)(long)pc_addr );
	HCFASSERT( ((long)pc_addr & (HCF_ALIGN-1) ) == 0, HCF_ALIGN);
	HCFASSERT( (offset & 01) == 0, offset )
#if HCF_BIG_ENDIAN
	HCFASSERT( word_len == 0 || ((long)pc_addr & 1 ) == 0, (hcf_16)(long)pc_addr )
	HCFASSERT( word_len <= tot_len, word_len )
#endif // HCF_BIG_ENDIAN
/*2*/
	if ( ( rc = ifbp->IFB_DefunctStat ) == HCF_SUCCESS ) {
/*4*/	OPW( HREG_SELECT_1, fid );
		OPW( HREG_OFFSET_1, offset & 0xFFFE );
		HCF_WAIT_WHILE( IPW( HREG_OFFSET_1) & HCMD_BUSY );
/*6*/	HCFASSERT( !( IPW( HREG_OFFSET_1) & HREG_OFFSET_ERR ), offset )
		HCFASSERT( !( IPW( HREG_OFFSET_1) & HREG_OFFSET_ERR ), fid )
#if HCF_PROT_TIME
		if ( prot_cnt == 0 ) {
			HCFASSERT( DO_ASSERT, fid )
			HCFASSERT( DO_ASSERT, offset )
			rc = ifbp->IFB_DefunctStat = HCF_ERR_DEFUNCT_TIME_OUT;
			ifbp->IFB_CardStat |= CARD_STAT_DEFUNCT;
			IF_TALLY( ifbp->IFB_HCF_Tallies.MiscErr++; )
		}
#endif // HCF_PROT_TIME
		if ( tot_len ) {
#if HCF_BIG_ENDIAN
			i = word_len;
/*8*/		while ( i-- ) {
				if ( type == IO_IN ) *pc_addr = IN_PORT_WORD( io_port );
				else OUT_PORT_WORD( io_port, *pc_addr );		//@@presume no side effect on pc_addr
				pc_addr++;										//@@now we get the right increment (by 2))
			}
			i = tot_len - word_len;
#else
			i = tot_len;
#endif // HCF_BIG_ENDIAN
			cp = (wci_bufp)pc_addr;								/* @@now we can correctly initialize the
																 * (awkward) char pointer on BE as well as LE
																 * note that the STRING macros are defined
																 * as using char pointer to allow arbitrary
																 * alignment of the buffers supplied by MSF
																 * or higher layer
																 */
																/* some people make nasty macros, better be
																   prepared by turning them into compound
																   statements
																  */
			if ( type == IO_IN ) { IN_PORT_STRING_16( io_port, cp, i ); }
			else { OUT_PORT_STRING_16( io_port, cp, i ); }
		}
		if ( IPW( HREG_SW_0 ) != HCF_MAGIC ) rc =  HCF_ERR_NO_NIC;
	}
/*10*/
	if ( rc != HCF_SUCCESS && type == IO_IN ) {
#if HCF_BIG_ENDIAN
		pc_addr -= word_len;			//@@ restoration of pc_addr may be needed in case of BE
#endif // HCF_BIG_ENDIAN
		for ( i = tot_len; i; i-- ) *pc_addr++ = 0;
	}
	HCFTRACE( ifbp, HCF_TRACE_STRIO | HCF_TRACE_EXIT );
	return rc;
} // strio_16
#endif // HCF_TYPE_USB


/************************************************************************************************************
*
*.MODULE		update_mic_dummy / update_mic
*.DESCRIPTION
*
*.ARGUMENTS
*	void update_mic/update_mic_dummy( hcf_32* p, hcf_32 m )
*
*.RETURNS
*	void
*
*.NARRATIVE
*
* Parameters:
*	p		address of the MIC
*	m		32 bit value to be processed by the MIC calculation engine
*
*  Description:
*	update_mic_dummy is used when MIC is not yet / not anymore active. Using a dynamic routine pointer, which
*	is updated once per Tx or Rx frame, is supposed to be a faster and more elegant implementation then
*	determining whether or not to perform an actual MIC calculation a number of times per Rx or Tx frame.
*
*	update_mic is the implementation of the MIC algorithm. It is a monkey-see monkey-do copy of
*	Michael::appendByte()
*	of Appendix C of
*
*	The MIC is located in the IFB (to alleviate the byte versus double-word boundary problems).
*	The MIC is seperate for Tx and Rx, thus allowing hcf_send_msg to occur between hcf_service_nic and
*	hcf_rcv_msg.
*.DIAGRAM
*
*.NOTICE
*.ENDOC				END DOCUMENTATION
*
************************************************************************************************************/
void
update_mic_dummy( hcf_32* p, hcf_32 m )
{
	/*NOP*/;
} // update_mic_dummy

#if (HCF_TYPE) & HCF_TYPE_WPA

#define ROL32( A, n ) ( ((A) << (n)) | ( ((A)>>(32-(n)))  & ( (1UL << (n)) - 1 ) ) )
#define ROR32( A, n ) ROL32( (A), 32-(n) )

#define L	*p
#define R	*(p+1)

void
update_mic( hcf_32* p, hcf_32 m )
{
#if HCF_BIG_ENDIAN
	m = (m >> 16) | (m << 16);
#endif
		L ^= m;
		R ^= ROL32( L, 17 );
		L += R;
		R ^= ((L & 0xff00ff00) >> 8) | ((L & 0x00ff00ff) << 8);
		L += R;
		R ^= ROL32( L, 3 );
		L += R;
		R ^= ROR32( L, 2 );
		L += R;
} // update_mic
#undef R
#undef L
#endif // HCF_TYPE_WPA



//#if 0 //POtentially useful tidbits to remember to set in updated comments on a more appropriate place
///**********************************************************************************************************
//	As side effect of the Hermes Initialize command, the interrupts are disabled (The Hermes Register HREG_INT_EN == 0)
//
// -: In some environments (e.g. Windows NT with SystemSoft extensions to support Suspend/Resume the I/O space
//	is not mapped for a prolonged time after Resume. This causes the HCF to fail Busy bit waits etc after
//	approx 2.5 seconds. Since at higher levels there is a 2 second recovery timer, there is a retry immediately,
//	causing the system to hang for 20 seconds (in the view of the end-user). By checking whether there is
//	at least something with a "write&read back" characteristic, the HCF verifies whether the Hermes is present.
//	Note that while the I/O space is not yet mapped, this test fails, when the I/O space is mapped and a Hermes
//	is present (and stabilized after power up, but regardless of the Initialize command), this test succeeds.
//	If the test succeeds because the wrong card is mapped, this does not matter within the problem at hand.
// -:	Preset the return code at Time out.
//	The additional complication that no calibrated value for the protection count can be assumed since calibrate()
//	may not yet have determined a calibrated value (a catch 22) is handled by the initial value set at INI_TICK_INI
//	by hcf_connect). This approach is considered safe, because:
//	 o the HCF does not use the pipeline mechanism of Hermes commands.
//	 o the likelihood of failure (the only time when protection count is relevant) is small.
//	 o the time will be sufficiently large on a fast machine (busy bit drops on good NIC before counter expires)
//	 o the time will be sufficiently small on a slow machine (counter expires on bad NIC before the end user
//	   switches the power off in despair
//	The time needed to wrap a 32 bit counter around is longer than many humans want to wait, hence the more or
//	less arbitrary value of 0x40000L is chosen, assuming it does not take too long on an XT and is not too short on
//	a scream-machine.
//	Note that early models of the Hermes needed some arbitrary read before the first write activity to operate
//	stable (ref tracker 27). This busy drop wait achieves that function as a side effect.
//	If the Hermes passes the superficial health test of waiting for the Busy bit drop, the Hermes Initialize
//	command is executed. The Initialize command acts as the "best possible" reset under HCF control. A more
//	"severe/reliable" reset is under MSF control via the COR register.
// -:	Initialize the Hermes. In order to "roll back" from AP mode to STA mode, the undocumented bit 0x0100 must be
//	set. This bit is controlled via the cntl parameter of init.
//	The Hermes H/W controlled bits (i.e. HREG_EV_STAT) are not guaranteed to be cleared as result of the initialize
//	command. However it is guaranteed that no more events are in the pipeline. Ack-ing indiscriminately all events
//	resolves this problem. An alternative would be to use the resulting value of
//	"IPW( HREG_EV_STAT )" rather than 0xFFFF to specifically only reset those events
//	which are set. This strategy is considered to be only more aesthetically pleasing (if that).
//#endif // 0 useful tidbits
//













