
//   vim:tw=110:ts=4:
/**************************************************************************************************************
*
* FILE   :	DHF.C
*
* DATE	:	$Date: 2004/03/31 18:01:09 $   $Revision: 1.3 $
* Original	:	2004/03/18 16:36:15    Revision: 1.25      Tag: t20040318_02
* Original	:	2004/03/18 09:34:56    Revision: 1.24      Tag: t20040318_01
*
* AUTHOR :	John Meertens
*			Nico Valster
*
* SPECIFICATION: ........
*
* DESC   :	generic functions to handle the download of NIC firmware
*			Local Support Routines for above procedures
*
*			Customizable via HCFCFG.H, which is included by HCF.H
*
*
*	DHF is (intended to be) platform-independent.
*	DHF is a module that provides a number of routines to download firmware
*	images (the names primary, station, access point, secondary and tertiary
*	are used or have been used) to volatile or nonvolatile memory
*	in WaveLAN/IEEE NICs. To achieve this DHF makes use of the WaveLAN/IEEE
*	WCI as implemented by the HCF-module.
*
*	Download to non-volatile memory is used to update a WaveLAN/IEEE NIC to new
*	firmware. Normally this will be an upgrade to newer firmware, although
*	downgrading to older firmware is possible too.
*
***************************************************************************************************************
 *
 *
 * 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 © 1999 - 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.
 *
 *
**************************************************************************************************************/

#include "hcf.h"
#include "hcfdef.h"
#include "dhf.h"
#include "mmd.h"

//to distinguish MMD from HCF asserts by means of line number
#undef	FILE_NAME_OFFSET
#define FILE_NAME_OFFSET MMD_FILE_NAME_OFFSET
/*-----------------------------------------------------------------------------
 *
 * Defines, data structures, and global variables
 *
 *---------------------------------------------------------------------------*/

//                    12345678901234
char signature[14] = "FUPU7D37dhfwci";

//The binary download function "relocates" the image using constructions like:
//	fw->identity = (CFG_IDENTITY_STRCT FAR *)((char FAR *)fw->identity + (hcf_32)fw );
//under some of the memory models under MSVC 1.52 these constructions degrade to 16-bits pointer arithmetic.
//fw->identity is limited, such that adding it to fw, does not need to carry over from offset to segment.
//However the segment is not set at all.
//As a workaround the PSEUDO_CHARP macro is introduced which is a char pointer except for MSVC 1.52, in
//which case we know that a 32-bit quamtity is adequate as a pointer.
//Note that other platforms may experience comparable problems when using the binary download feature.
#if _MSC_VER ==  800				// Visual C++ 1.5
#define PSEUDO_CHARP hcf_32
#else
#define PSEUDO_CHARP hcf_8*
#endif

/*-----------------------------------------------------------------------------
 *
 * LTV-records retrieved from the NIC to:
 *	- determine compatibilty between NIC and image
 *	- ((setup the buffer size dynamically for non-volatile download (see note below) ))
 *	- supply plugging information contained in the PDA (H-I only)
 *
 *---------------------------------------------------------------------------*/

//!#define DL_SIZE 8192	//;? for the time being it seems simpler than using the "fixed anyway" CFG_DL_BUF_STRCT
// for USB/H1 we need a smaller value than 8192, 2000 is the 'correct' value; rest will simply follow this.
#define DL_SIZE 2000

//CFG_IDENTITY_STRCT   	pri_identity	= { LOF(CFG_IDENTITY_STRCT), CFG_PRI_IDENTITY };
CFG_SUP_RANGE_STRCT 	mfi_sup        	= { LOF(CFG_SUP_RANGE_STRCT), CFG_NIC_MFI_SUP_RANGE };
CFG_SUP_RANGE_STRCT 	cfi_sup        	= { LOF(CFG_SUP_RANGE_STRCT), CFG_NIC_CFI_SUP_RANGE };
/* Note: could be used rather than the above explained and defined DL_SIZE if need arises
 * CFG_DL_BUF_STRCT    	dl_buf         	= { LOF(CFG_DL_BUF_STRCT), CFG_DL_BUF };
*/
#if !( (HCF_TYPE) & HCF_TYPE_HII )
hcf_16              	cfg_prod_data[PDA_SIZE] = { PDA_SIZE-1, CFG_PROD_DATA };
#endif // HCF_TYPE_HII

/*-----------------------------------------------------------------------------
 * Array ltv_info stores NIC information (in the form of LTV-records)
 * needed for download. A NULL record indicates the end of the array.
 *---------------------------------------------------------------------------*/

/* The LTV_INFO_STRUCT is needed to save the sizes of the structs, because after a GET_INFO()
 * the len field is changed to the real len of the RID by the called routine.
 * This is only relevant if the DHF used without reloading the driver/utility.
 */
typedef struct {
	LTVP 	ltvp;
	hcf_16	len;
} LTV_INFO_STRUCT , *LTV_INFO_STRUCT_PTR;


LTV_INFO_STRUCT ltv_info[] = {
//	{ (LTVP)&pri_identity,		LOF(CFG_IDENTITY_STRCT) } ,
	{ (LTVP)&mfi_sup,			LOF(CFG_SUP_RANGE_STRCT) } ,
	{ (LTVP)&cfi_sup,			LOF(CFG_SUP_RANGE_STRCT) } ,
//	{ (LTVP)&dl_buf,			LOF(CFG_DL_BUF_STRCT) } ,  see earlier comment
#if !( (HCF_TYPE) & HCF_TYPE_HII )
	{ (LTVP)&cfg_prod_data[0], 	PDA_SIZE-1 } ,
#endif // HCF_TYPE_HII
	{ (LTVP) NULL, 				0 }
};


/*-----------------------------------------------------------------------------
 *
 * Local service function prototypes
 *
 *---------------------------------------------------------------------------*/

static hcf_16			*apply_plug_rules( hcf_16 *pdap, hcf_16 code );
static int				check_comp_fw( memimage *fw );
static void 			mem_cpy( hcf_16 FAR *dp, hcf_16 FAR *sp, int cnt );
#if !( (HCF_TYPE) & HCF_TYPE_HII )
static hcf_16			calc_crc( hcf_8  *cp, int count);
static int				check_pda_crc( void );
static CFG_PROG_STRCT 	*find_addr_in_image( memimage *fw, hcf_32 addr );
static int				plug_production_data( memimage *fw );
#endif // HCF_TYPE_HII


/*-----------------------------------------------------------------------------
 *
 * Service routines
 *
 *---------------------------------------------------------------------------*/


/*************************************************************************************************************
* .MODULE:	calc_crc
* .DESCRIPTION	Calculate the crc of a given number of bytes.
*
* .ARGUMENTS
*	hcf_16 calc_crc( hcf_8 *cp, int count )
*
* .RETURNS
*	hcf_16  - the CRC
*
* .NARRATIVE
*
* Parameters:
*	*cp  	pointer to the starting point of the crc calculation.
*	count	number of bytes to calculate over
*
* Description:
*
* .DIAGRAM
*
* .NOTICE
* .ENDOC				END DOCUMENTATION
*
*************************************************************************************************************/
#if !( (HCF_TYPE) & HCF_TYPE_HII )
hcf_16
calc_crc( hcf_8 *cp, int count )
{
hcf_16  crcvalue = 0;	/* The CRC to return */
hcf_16  crctmp;			/* temporary storage for the CRC */
int		wCarry;			/* Carry-over for the CRC calculation */
int		i;

	// Loop through all bytes of the area over which to do calculation
	while ( count-- ) {
		crctmp = ( crcvalue ^ *cp++ ) & 0x00ff;
		crcvalue >>= 8;

		/* XOR with polynomial for every '1' in the byte */
		for ( i= 0; i< 8; i++ )  {
			/* Now shift all bits right */
			wCarry = crctmp & 0x0001;           	/* Remember if there is a carry-over */
			crctmp >>= 1;
			if ( wCarry == 1 ) crctmp ^= 0xa001;	/* XOR with polynomial */
		}
		crcvalue ^= crctmp;
	}
	return crcvalue;
} // calc_crc
#endif // HCF_TYPE_HII


/*************************************************************************************************************
*
* .MODULE:	check_comp
* .DESCRIPTION	Checks compatibility of an actor with a supplier
*
* .RETURNS
*	 0  same version
*	 1  compatible but supplier is newer than actor
*   -1  compatible but supplier is older than actor
*   -2  not compatible:
*   	+ the roles where different;
*   	+ the interface ID where not the same;
*   	+ the actor did not have a variant mentioned in the supplier range
*
* .NARRATIVE
*
* Parameters:
*
* Description:
*   Checks compatibility of an actor with a supplier. To achieve this, each of
*   the actor variants is checked against the (single) supplier bottom-top range
*   till either an acceptable match is found or all actor variants have been
*   tried. The compatibilities are defined in ....;?
*   If overlap exists for a variant of the actor, we check whether the actor is
*   identical, or older, or younger by comparing the value of the tops of the
*   supplier and the actor. The bottom value is not considered in this check.
*************************************************************************************************************/



/************************************************************************************************************
* .MODULE:    	check_comp_fw( memimage *fw )
* .DESCRIPTION	Checks compatibility of CFI and MFI, NIC as supplier, station/AP firmware image as supplier
*
* .ARGUMENTS
*	int check_comp_fw(memimage *fw)
*
* .RETURNS
*   HFC_SUCCESS      	- firmware OK
*   DHF_ERR_INCOMP_FW
*
* .NARRATIVE
*
* Parameters:
*	memimage *fw
*
* Description:
*   This function uses compatibility and identity information that has been
*   retrieved from the card which is currently inserted to check whether the
*   station firmware image to be downloaded is compatible.
*************************************************************************************************************/
int
check_comp_fw( memimage *fw )
{
CFG_RANGE20_STRCT  		*p;
int   					rc = HCF_SUCCESS;
CFG_RANGE_SPEC_STRCT*	i;

	switch( fw->identity->typ ) {
	  case CFG_FW_IDENTITY:				//Station F/W
	  case COMP_ID_FW_AP_FAKE:			//;?is this usefull (used to be:  CFG_AP_IDENTITY)
		break;
	  default:
		MMDASSERT( DO_ASSERT, fw->identity->typ ) 	//unknown/unsupported firmware_type:
		rc = DHF_ERR_INCOMP_FW;
		return rc; /* ;? how usefull is this anyway,
					*  till that is sorted out might as well vioalte my own single exit principle
					*/
	}
	p = fw->compat;
	i = NULL;
	while( p->len && i == NULL ) {					// check the MFI ranges
		if ( p->typ  == CFG_MFI_ACT_RANGES_STA ) {
			i = mmd_check_comp( (void*)p, &mfi_sup );
		}
		p++;
	}
	MMDASSERT( i, 0 )	//MFI: NIC Supplier not compatible with F/W image Actor
	if ( i ) {
		p = fw->compat;
		i = NULL;
		while ( p->len && i == NULL ) {			// check the CFI ranges
			if ( p->typ  == CFG_CFI_ACT_RANGES_STA ) {
				 i = mmd_check_comp( (void*)p, &cfi_sup );
			}
			p++;
		}
		MMDASSERT( i, 0 )	//CFI: NIC Supplier not compatible with F/W image Actor
	}
	if ( i == NULL ) {
		rc = DHF_ERR_INCOMP_FW;
	}
	return rc;
} // check_comp_fw


#if !( (HCF_TYPE) & HCF_TYPE_HII )
/*************************************************************************************************************
*
* .MODULE:	check_pda_crc
* .DESCRIPTION	Checks CRC in Production Data Area
*
* .ARGUMENTS
*	static int check_pda_crc( void )
*
* .RETURNS
*   HCF_SUCCESS 		- CRC correct
*   DHF_ERR_DOWNLOAD	- CRC not correct or PDA_END record not found
*
* .NARRATIVE
*
* Parameters:
*  N/A
*
* Description:
*   Locates crc value in PDA. Calculates what CRC should be. Compares the
*   calculated value with the value found and returns result of comparison.
*************************************************************************************************************/
static int
check_pda_crc( void )
{
	hcf_16  crc;
	hcf_16  *pp;
	LTVP	p = (LTVP)&cfg_prod_data[0];
	int 	rc = DHF_ERR_DOWNLOAD;

	pp = find_record_in_pda(&cfg_prod_data[2], PDA_END );
	MMDASSERT( pp, 0) 	//PDA_END not Found
	if ( pp ) {
		pp = ((LTVP)pp)->val;
		crc = calc_crc( (hcf_8*)p->val, (int)(pp - p->val) * sizeof(hcf_16) );
		if ( crc == CNV_LITTLE_TO_INT(*pp) ) {
			rc = HCF_SUCCESS;
		}
		MMDASSERT( rc == HCF_SUCCESS, CNV_LITTLE_TO_INT(*pp) ) 	//PDA CRC Error
	}

	return rc;
} // check_pda_crc
#endif // HCF_TYPE_HII


#if !( (HCF_TYPE) & HCF_TYPE_HII )
/*************************************************************************************************************
*
* .MODULE:	find_addr_in_image
* .DESCRIPTION	Looks for a memblock in the firmware image that includes the
*          	address passed in as parameter
*
* .ARGUMENTS
*	static CFG_PROG_STRCT * find_addr_in_image( memimage *fw, hcf_32 addr   )
*
* .RETURNS
*   A pointer to the memblock that includes the address, or NULL if the address is not found.
*
* .NARRATIVE
*
* Parameters:
*	fw:		firmware image containing address
* 	addr:	address to be located
*
* Description:
*   Follow the list of memory blocks in the firmware image until the addr
*   searched for lies within the block.
*************************************************************************************************************/
static CFG_PROG_STRCT *
find_addr_in_image( memimage *fw, hcf_32 addr   )
{

CFG_PROG_STRCT	*p = fw->codep;

	MMDASSERT( p, 0)

	while ( p->len) {
		if ( p->mode == CFG_PROG_ADDR	&&
			 p->nic_addr <= addr		&&
			 addr <= p->nic_addr + p->segment_size ) break;
		p++;
	}
	if ( p->len == 0 ) {
		p = NULL;
		MMDASSERT( p, (hcf_16)(addr >> 16 ) ) 	//address not found in image
		MMDASSERT( p, (hcf_16)addr )
	}
	return p;
} // find_addr_in_image
#endif // HCF_TYPE_HII


/*************************************************************************************************************
*
* .MODULE:	find_record_in_pda
* .DESCRIPTION	Locates a production data record with specified code in the
*          	production data area.
*
* .ARGUMENTS
*	hcf_16 * find_record_in_pda(hcf_16 *pdap, hcf_16 code )
*
* .RETURNS
*   Pointer to record if code found, else NULL
*
* .NARRATIVE
*
* Parameters:
* hcf_16 *pdap
* hcf_16 code
*
* Description:
*   Follow the list of PDA (of type LTV) records until the typ-field of the
*   LTV-record is equal to the code parameter, or until the end of the PDA is
*   reached (The end of the PDA is indicated by a record with code 0).
*   If the len parameter is not NULL then there are two possibilities:
*   1. if len == 0 the length of the record found is returned in len
*   2. if len != 0 the length of the record found is compared with len; if not
*  	equal then the record found is not correct and NULL is returned
*
*   hcf_16 *pdap,   // pointer to production data area
*   hcf_16 code,	// code of production data record to be located
*   hcf_16 *len 	// length of the record found:
*   				// - set to required length or zero if any length is OK
*   				// - set to NULL if not needed
 *************************************************************************************************************/
#if !( (HCF_TYPE) & HCF_TYPE_HII )
hcf_16 *
find_record_in_pda(hcf_16 *pdap, hcf_16 code )
{

hcf_16	pdac = PDA_END;
hcf_16*	pdaheadp = pdap;

	MMDASSERT( pdap, 0 )

	//+1 in the following line as we look at the typ-field of the LTV-record
	//NATIE (see note)
	while((pdap < (pdaheadp + PDA_SIZE - 2)) && (( pdac = CNV_LITTLE_TO_INT(*(pdap+1)) ) != PDA_END) ) {
		if(pdac == code) {
			break;
		}
		pdap += CNV_LITTLE_TO_INT(*pdap) + 1;
	}
/* NOTE: If a corrup PDA is read this while loop might pass the end of the
 * PDA structure and causes crashes - blue screen on NT. Since *pdap is
 * passed as a pointer to a hcf_16 we dont now the length of the remaining
 * PDA but assume that it is PDA_SIZE - 2. This logic is also follow in
 * other places of this module.
 */

	if ( pdac == code ) {
		/*used to be some logic based on len being NULL or not, but len was always NULL*/;
	} else {
		pdap = NULL;
	}
	return pdap;
} // find_record_in_pda
#endif // HCF_TYPE_HII

/*************************************************************************************************************
*
* .MODULE			void mem_cpy( hcf_16 FAR *dp, hcf_16 FAR *sp, int cnt )
* .DESCRIPTION	copies cnt words from sp to dp without using a lib function
*
* .ARGUMENTS
*	void mem_cpy( hcf_16 FAR *dp, hcf_16 FAR *sp, int cnt )
*
* .RETURNS
*
* .NARRATIVE
* Parameters:
*  hcf_16 FAR *dp
*  hcf_16 FAR *sp
*  int cnt
*
* .DIAGRAM
*
* .NOTICE
*
**************************************************************************************************************/
void
mem_cpy( hcf_16 FAR *dp, hcf_16 FAR *sp, int cnt )
{

	while ( cnt-- )	*dp++ = *sp++;

 } // mem_cpy


/*************************************************************************************************************
*
* .MODULE:	plug_production_data
* .DESCRIPTION	Plug firmware image with production data records
*
* .ARGUMENTS
*	static int plug_production_data(memimage *fw)
*
* .RETURNS
*   HCF_SUCCESS
*   DHF_ERR_DOWNLOAD
*
* .NARRATIVE
*
* Parameters:
* memimage *fw
*
* Description:
*   For all records to be plugged
*     Locate position of plug record
*     Apply special plugging rules if necessary
*     If enough room is available to fit in the plug record then
*       Locate the position of the plug record in the image to be downloaded
*       Copy the contents of NIC-record to the image
*     Else
*       Return DHF_ERR_DOWNLOAD
*************************************************************************************************************/
#if !( (HCF_TYPE) & HCF_TYPE_HII )
static int
plug_production_data( memimage *fw )
{
plugrecord  	*plugrecordp = fw->pdaplug;
int         	rc = HCF_SUCCESS;
int				plugrc = HCF_SUCCESS;
CFG_PROG_STRCT	*ltvp;
hcf_32      	code; 				// Code to plug
hcf_16      	*pdap;				// pointer to matching code found in pda

	MMDASSERT( plugrecordp, 0)

	//Scan through all plugrecords in firmware image
	while( ( rc == HCF_SUCCESS ) && ( code = plugrecordp->code ) != 0 ) {
		pdap = apply_plug_rules(&cfg_prod_data[2], (hcf_16)(code & CODEMASK) );
		if ( pdap ) {
			if ( (CNV_LITTLE_TO_INT(*pdap) - 1) * 2 != plugrecordp->len ) {
				//!! Be aware of the difference with primary plug records:
				//!! as opposed to plug_pri_records '!=' rather than '>'
				//!! production data plug records must fit exactly at their location
				MMDASSERT( DO_ASSERT,(hcf_16)(code & CODEMASK) )	//size mismatch Plug Data Area
				rc = DHF_ERR_DOWNLOAD;
			} else {
				if ( ( ltvp = find_addr_in_image( fw, plugrecordp->addr ) ) != NULL ) {
					mem_cpy( (hcf_16*)&ltvp->host_addr[ plugrecordp->addr - ltvp->nic_addr],
							  pdap+2, (int)plugrecordp->len / 2 );
				}
			}
		} else if ( (code & CODEMASK ) < 0xFF00 ) {
			MMDASSERT(DO_ASSERT, (hcf_16) code) 	//Error in plugging
			rc = DHF_ERR_DOWNLOAD;
		}
		plugrecordp++;
	}
	return rc;
} // plug_production_data
#endif // HCF_TYPE_HII

/*-----------------------------------------------------------------------------
 *
 * Exported functions
 *
 *---------------------------------------------------------------------------*/

//#ifdef DSF_BINARY_FILE


/*************************************************************************************************************
*
* .MODULE: dhf_download_binary
* .DESCRIPTION Downloads a complete (primary, station, or access point) firmware
*       	image to the NIC.
*
* .ARGUMENTS
*	int dhf_download_binary( memimage *fw )
*
* .RETURNS
*   HCF_SUCCESS         	- download completed successfully.
*   HCF_ERR_NO_NIC      	- no NIC present in slot
*   DHF_ERR_INCOMP_FW		- firmware not compatible
*   DHF_ERR_DOWNLOAD    	- some other problem occurred in download (condensed)
*
* .NARRATIVE
*
* Parameters:
*   memimage *fw             	station image to be downloaded
*
* Description:
*
*
* NOTICE:
*
*************************************************************************************************************/
int
dhf_download_binary( memimage *fw )
{
int 				rc = HCF_SUCCESS;
CFG_PROG_STRCT 		*p;
int					i;

//can't assert since this routine is called before hcf_connect	MMDASSERT( DO_ASSERT, 0xCAF4)			//just to proof that the complete assert machinery is working

	//validate the image
	for ( i = 0; i < sizeof(signature) && fw->signature[i] == signature[i]; i++ ) /*NOP*/;
	if ( i != sizeof(signature) 		||
		 fw->signature[i] != 0x01   	||
		 //test for Little/Big Endian Binary flag
		 fw->signature[i+1] != ( /*HCF_BIG_ENDIAN ? 'B' : */ 'L' ) ) rc = DHF_ERR_INCOMP_FW;
	else {					//Little Endian Binary format
		fw->codep    = (CFG_PROG_STRCT FAR *)((PSEUDO_CHARP)fw->codep + (hcf_32)fw );
		fw->identity = (CFG_IDENTITY_STRCT FAR *)((PSEUDO_CHARP)fw->identity + (hcf_32)fw );
//#if ! ( (HCF_TYPE) & HCF_TYPE_HII )
		fw->pdaplug  = (plugrecord FAR *)((PSEUDO_CHARP)fw->pdaplug + (hcf_32)fw );
		fw->priplug  = (plugrecord FAR *)((PSEUDO_CHARP)fw->priplug + (hcf_32)fw );
//#endif // HCF_TYPE_HII
		fw->compat   = (CFG_RANGE20_STRCT FAR *)((PSEUDO_CHARP)fw->compat + (hcf_32)fw );
		p = fw->codep;
		while ( p->len ) {
			p->host_addr = (char FAR *)((PSEUDO_CHARP)p->host_addr + (hcf_32)fw);
			p++;
		}
	}
	return rc;
}   // dhf_download_binary

//#endif // DSF_BINARY_FILE

/*************************************************************************************************************
*
* .MODULE: 		dhf_download_firmware
* .DESCRIPTION	Downloads a complete (primary or tertiary) firmware image to the NIC.
*
* .ARGUMENTS
*	int dhf_download_fw( void *ifbp, memimage *fw )
*
* .RETURNS
*	HCF_SUCCESS        	- download completed successfully.
*	HCF_ERR_NO_NIC     	- no NIC present
*	DHF_ERR_INCOMP_FW 	- firmware not compatible
*	DHF_ERR_DOWNLOAD   	- some other problem occurred in download
*
* .NARRATIVE
*
* Parameters
*   *ifbp		pointer to the ifb; ignored when using the UIL
*   *fw     	station image to be downloaded
*
* Description:
* -	check the signature of the image
* -	get the compatibility information from the components on the NIC
*	  - Primary Firmware Identity
*	  -	Modem - Firmware I/F
*	  -	Controller - Firmaware I/F
* -	if necessary ( i.e. H-I) get the PDA contents from the NIC
* -	check the compatibilty of the MFI and CFI of the NIC with the F/W image
*	Note: the Primary F/W compatibilty is only relevant for the "running" HCF and is already verified in
*	hcf_connect
* -	if necessary ( i.e. H-I)
*	  -	verify the sumcheck of the PDA
*	  -	plug the image (based on the PDA and the default plug records)
* -	loop over all the download LTVs in the image which consists of a sequence of
*	  - CFG_PROG_VOLATILE/CFG_PROG_NON_VOLATILE
*	  - 1 or more sequences of CFG_PROG_ADDR, CFG_PROG_DATA,....,CFG_PROG_DATA
*	  -	CFG_PROG_STOP
*
* NOTICE:
* The old comment was:
*	// Download primary firmware if necessary and allowed. This is done silently (without telling
*	// the user) and only if the firmware in the download image is newer than the firmware in the
*	// card.  In Major version 4 of the primary firmware functions of Hermes and Shark were
*	// combined. Prior to that two seperate versions existed. We only have to download primary
*	// firmware if major version of primary firmware in the NIC < 4.
*	//		download = pri_identity.version_major < 4;
*	//		if ( download ) {
*	//			rc = check_comp_primary( fw );
*	//		}
* It is my understanding that Pri Variant 1 must be updated by Pri Variant 2. The test on
* major version < 4 should amount to the same result but be "principally" less correct
* In deliberation with the Architecture team, it was deciced that this upgrade for old H-I
* NICs, is an aspect which belongs on the WSU level not on the DHF level
*
*************************************************************************************************************/
int
dhf_download_fw( void *ifbp, memimage *fw )
{
int 				rc = HCF_SUCCESS;
LTV_INFO_STRUCT_PTR pp = ltv_info;
CFG_PROG_STRCT 		*p = fw->codep;
LTVP 				ltvp;
int					i;
#if !((HCF_TYPE) & HCF_TYPE_HII)
hcf_16				size, size_org;
char FAR *			addr_org;
#endif // HCF_TYPE_HII

	MMDASSERT( DO_ASSERT, 0xCAF3)	//just to proof that the complete assert machinery is working
	MMDASSERT( fw != NULL, 0)

	//validate the image
	for ( i = 0; i < sizeof(signature) && fw->signature[i] == signature[i]; i++ ) /*NOP*/;
	if ( i != sizeof(signature) 		||
		 fw->signature[i] != 0x01		||
		 //check for binary image
		 ( fw->signature[i+1] != 'C' && fw->signature[i+1] != ( /*HCF_BIG_ENDIAN ? 'B' : */ 'L' ) ) )
		 rc = DHF_ERR_INCOMP_FW;

//	Retrieve all information needed for download from the NIC
	while ( ( rc == HCF_SUCCESS ) && ( ( ltvp = pp->ltvp) != NULL ) ) {
		ltvp->len = pp++->len;	// Set len to origional len. This len is changed to real len by GET_INFO()
		rc = GET_INFO( ltvp );
		MMDASSERT(rc == HCF_SUCCESS, rc)
		MMDASSERT(rc == HCF_SUCCESS, ltvp->typ)
		MMDASSERT(rc == HCF_SUCCESS, ltvp->len)
	}
	if ( rc == HCF_SUCCESS ) rc = check_comp_fw( fw );
	if ( rc == HCF_SUCCESS ) {
#if (HCF_TYPE) & HCF_TYPE_HII		//;?if H-II is to be supported on USB-dongle, might as well integrate this
									//  with the cutting in pieces for H-I
		while ( rc == HCF_SUCCESS && p->len ) {
			rc = PUT_INFO( p );
			p++;
		}
#else
		if ( ( rc = check_pda_crc() ) 				== HCF_SUCCESS &&
			 ( rc = plug_production_data( fw ) )	== HCF_SUCCESS ) {
			rc = PUT_INFO( p );					//CFG_PROG_VOLATILE
			p++;
		}
		while ( rc == HCF_SUCCESS && p->len ) {
			if ( p->flags & 0x0001 /*;?MNEMONIC*/ ) {
				*(hcf_16*)&p->host_addr[p->segment_size - 2] = calc_crc( p->host_addr, p->segment_size - 2);
			}
			rc = PUT_INFO( p );					//CFG_PROG_ADDR or CFG_PROG_STOP
			if ( p->mode != CFG_PROG_ADDR ) break;

			size_org = size = p->segment_size;
			addr_org = p->host_addr;
			p->mode = CFG_PROG_DATA;

			while ( size && rc == HCF_SUCCESS ) {
				p->segment_size = size > DL_SIZE ? DL_SIZE : size;
				rc = PUT_INFO( p );				//CFG_PROG_DATA
				p->host_addr += p->segment_size;
				size -= p->segment_size;
			}
			p->segment_size = size_org;
			p->host_addr = addr_org;
			p->mode = CFG_PROG_ADDR;
			p++;
		}
#endif // HCF_TYPE_HII
	}
	MMDASSERT(rc == HCF_SUCCESS, rc)
	return rc;
}   // dhf_download_fw


//============================== this used to be a seperate file dhfplug.c


#if !( (HCF_TYPE) & HCF_TYPE_HII )

/*-----------------------------------------------------------------------------
 * Default Plugging values
 *---------------------------------------------------------------------------*/

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[10];	//values
} byDefPDRRecord_0005 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0005)/sizeof(hcf_16) - 1 ),
	0x0005,
	0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00
};

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[4];		//values
} byDefPDRRecord_0108 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0108)/sizeof(hcf_16) - 1 ),
	0x0108,
	0x00, 0x00, 0x00, 0x00
};

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[10];	//values
} byDefPDRRecord_0109 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0109)/sizeof(hcf_16) - 1 ),
	0x0109,
	0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00
};

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[2];		//values
} byDefPDRRecord_0150 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0150)/sizeof(hcf_16) - 1 ),
	0x0150,
	0x00, 0x3F
};

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[28];	//values
} byDefPDRRecord_0160 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0160)/sizeof(hcf_16) - 1 ),
	0x0160,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

struct {
	hcf_16	len;		//lenght
	hcf_16	typ;		//UNUSED type, i.e. just a place holder
	hcf_8	val[256];	//values
} byDefPDRRecord_0161 = {
	CNV_INT_TO_LITTLE( sizeof(byDefPDRRecord_0161)/sizeof(hcf_16) - 1 ),
	0x0161,
	0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01,
	0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01, 0x3F, 0x01,
	0x3F, 0x01, 0x3E, 0x01, 0x3E, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3C, 0x01, 0x3C, 0x01, 0x3B, 0x01,
	0x3B, 0x01, 0x3A, 0x01, 0x3A, 0x01, 0x39, 0x01, 0x39, 0x01, 0x38, 0x01, 0x38, 0x01, 0x37, 0x01,
	0x37, 0x01, 0x36, 0x01, 0x36, 0x01, 0x35, 0x01, 0x35, 0x01, 0x34, 0x01, 0x34, 0x01, 0x33, 0x01,
	0x33, 0x01, 0x32, 0x01, 0x32, 0x01, 0x31, 0x01, 0x31, 0x01, 0x30, 0x01, 0x30, 0x01, 0x7B, 0x01,
	0x7B, 0x01, 0x7A, 0x01, 0x7A, 0x01, 0x79, 0x01, 0x79, 0x01, 0x78, 0x01, 0x78, 0x01, 0x77, 0x01,
	0x77, 0x01, 0x76, 0x01, 0x76, 0x01, 0x75, 0x01, 0x75, 0x01, 0x74, 0x01, 0x74, 0x01, 0x73, 0x01,
	0x73, 0x01, 0x72, 0x01, 0x72, 0x01, 0x71, 0x01, 0x71, 0x01, 0x70, 0x01, 0x70, 0x01, 0x68, 0x01,
	0x68, 0x01, 0x67, 0x01, 0x67, 0x01, 0x66, 0x01, 0x66, 0x01, 0x65, 0x01, 0x65, 0x01, 0x57, 0x01,
	0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01, 0x55, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01,
	0x53, 0x01, 0x52, 0x01, 0x52, 0x01, 0x51, 0x01, 0x51, 0x01, 0x50, 0x01, 0x50, 0x01, 0x48, 0x01,
	0x48, 0x01, 0x47, 0x01, 0x47, 0x01, 0x46, 0x01, 0x46, 0x01, 0x45, 0x01, 0x45, 0x01, 0x44, 0x01,
	0x44, 0x01, 0x43, 0x01, 0x43, 0x01, 0x42, 0x01, 0x42, 0x01, 0x41, 0x01, 0x41, 0x01, 0x40, 0x01,
	0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
	0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01
};


/*************************************************************************************************************
* .MODULE:	apply_plug_rules
* .DESCRIPTION	Locates the record with code in the PDA, and subsequently adjusts
*          	the record, if needed, by applying special plug-rule().
*
* .ARGUMENTS
*	hcf_16* apply_plug_rules( hcf_16 *pdap, hcf_16 code )
*
* .RETURNS 	HCF_SUCCESS  - if everthing OK
*
* .NARRATIVE
*
* Parameters:
* pdap   	- pointer to production data area.
* code   	- code of production data record to be (possibly) adjusted.
*
* Description:
*   This function will apply special plugging rules to the production data
*   record passed in, if such a special rule exists for the record.
*   In some cases it will create a new production data record.
*
* Notes:
* - This is an internal DHF-function, i.e. it does not occur in the DHF-
*   interface. It is not a static function as it is used in file DHF.C.
*************************************************************************************************************/
hcf_16*
apply_plug_rules( hcf_16 *pdap, hcf_16 code )
{
hcf_16 *record = find_record_in_pda( pdap, code );

	// Adaption code to handle Low Power NICs
	// If PDR 0110 - Modem REFDAC Set-up Values Normal requested
	if ( code == 0x110 ) {
		// If PDA contains PDR 0111 - Modem REFDAC Set-up Values Outdoor
		if ( ( record = find_record_in_pda( pdap, 0x111 ) ) == NULL ) {
			// If PDA contains PDR 0110 - Modem REFDAC Set-up Values Normal
			record = find_record_in_pda( pdap, 0x110 );
		}
	}
	if ( code == 0x120 ) {	//  Adaption code for PDR 0120 - Modem VGDAC Set-up Values Normal requested
		// If PDA contains PDR 0121 - Modem VGDAC Set-up Values Outdoor
		if ( ( record = find_record_in_pda( pdap, 0x121 ) ) == NULL ) {
			// If PDA contains PDR 0120 - Modem VGDAC Set-up Values Normal
			record = find_record_in_pda( pdap, 0x120 );
		}
	}
	if ( record == NULL ) {
		if ( code == 0x150 ) {		// Adaption code for PDR 0150 - Antenna diversity
			record = &byDefPDRRecord_0150.len;
		}
		if ( code == 0x160 ) {		// Adaption code for PDR 0160 - Modem VCO band Set-up
			record = &byDefPDRRecord_0160.len;
		}
		if ( code == 0x161 ) {		// Adaption code for PDR 0161 - Modem Rx Gain Table Values
			record = &byDefPDRRecord_0161.len;
		}
		if ( code == 0x005 ) {		// Adaption code for PDR 0005 - HWIF Compatiblity
			record = &byDefPDRRecord_0005.len;
		}
		if ( code == 0x108 ) {		// Adaption code for PDR 0108 - PPPPSign
			record = &byDefPDRRecord_0108.len;
		}
		if ( code == 0x109 ) {		// Adaption code for PDR 0109 - PPPPProf
			record = &byDefPDRRecord_0109.len;
		}
	}
	return record;
}   // apply_plug_rules

#endif // HCF_TYPE_HII
//============================== this used to be a seperate file dhfplug.c

