blob: b527a019188bac0c0a9ef3c2e9f6714aec26e6fc [file] [log] [blame]
/*
* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*
* File: vntwifi.c
*
* Purpose: export functions for vntwifi lib
*
* Functions:
*
* Revision History:
*
* Author: Yiching Chen
*
* Date: feb. 2, 2005
*
*/
#include "vntwifi.h"
#include "IEEE11h.h"
#include "country.h"
#include "device.h"
#include "wmgr.h"
#include "datarate.h"
//#define PLICE_DEBUG
/*--------------------- Static Definitions -------------------------*/
//static int msglevel =MSG_LEVEL_DEBUG;
//static int msglevel =MSG_LEVEL_INFO;
/*--------------------- Static Classes ----------------------------*/
/*--------------------- Static Variables --------------------------*/
/*--------------------- Static Functions --------------------------*/
/*--------------------- Export Variables --------------------------*/
/*--------------------- Export Functions --------------------------*/
/*+
*
* Description:
* Set Operation Mode
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* eOPMode - Opreation Mode
* Out:
* none
*
* Return Value: none
*
-*/
void
VNTWIFIvSetOPMode (
void *pMgmtHandle,
WMAC_CONFIG_MODE eOPMode
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
pMgmt->eConfigMode = eOPMode;
}
/*+
*
* Description:
* Set Operation Mode
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* wBeaconPeriod - Beacon Period
* wATIMWindow - ATIM window
* uChannel - channel number
* Out:
* none
*
* Return Value: none
*
-*/
void
VNTWIFIvSetIBSSParameter (
void *pMgmtHandle,
WORD wBeaconPeriod,
WORD wATIMWindow,
UINT uChannel
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
pMgmt->wIBSSBeaconPeriod = wBeaconPeriod;
pMgmt->wIBSSATIMWindow = wATIMWindow;
pMgmt->uIBSSChannel = uChannel;
}
/*+
*
* Description:
* Get current SSID
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* Out:
* none
*
* Return Value: current SSID pointer.
*
-*/
PWLAN_IE_SSID
VNTWIFIpGetCurrentSSID (
void *pMgmtHandle
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
return((PWLAN_IE_SSID) pMgmt->abyCurrSSID);
}
/*+
*
* Description:
* Get current link channel
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* Out:
* none
*
* Return Value: current Channel.
*
-*/
UINT
VNTWIFIpGetCurrentChannel (
void *pMgmtHandle
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
if (pMgmtHandle != NULL) {
return (pMgmt->uCurrChannel);
}
return 0;
}
/*+
*
* Description:
* Get current Assoc ID
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* Out:
* none
*
* Return Value: current Assoc ID
*
-*/
WORD
VNTWIFIwGetAssocID (
void *pMgmtHandle
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
return(pMgmt->wCurrAID);
}
/*+
*
* Description:
* This routine return max support rate of IES
*
* Parameters:
* In:
* pSupportRateIEs
* pExtSupportRateIEs
*
* Out:
*
* Return Value: max support rate
*
-*/
BYTE
VNTWIFIbyGetMaxSupportRate (
PWLAN_IE_SUPP_RATES pSupportRateIEs,
PWLAN_IE_SUPP_RATES pExtSupportRateIEs
)
{
BYTE byMaxSupportRate = RATE_1M;
BYTE bySupportRate = RATE_1M;
UINT ii = 0;
if (pSupportRateIEs) {
for (ii = 0; ii < pSupportRateIEs->len; ii++) {
bySupportRate = DATARATEbyGetRateIdx(pSupportRateIEs->abyRates[ii]);
if (bySupportRate > byMaxSupportRate) {
byMaxSupportRate = bySupportRate;
}
}
}
if (pExtSupportRateIEs) {
for (ii = 0; ii < pExtSupportRateIEs->len; ii++) {
bySupportRate = DATARATEbyGetRateIdx(pExtSupportRateIEs->abyRates[ii]);
if (bySupportRate > byMaxSupportRate) {
byMaxSupportRate = bySupportRate;
}
}
}
return byMaxSupportRate;
}
/*+
*
* Description:
* This routine return data rate of ACK packtet
*
* Parameters:
* In:
* byRxDataRate
* pSupportRateIEs
* pExtSupportRateIEs
*
* Out:
*
* Return Value: max support rate
*
-*/
BYTE
VNTWIFIbyGetACKTxRate (
BYTE byRxDataRate,
PWLAN_IE_SUPP_RATES pSupportRateIEs,
PWLAN_IE_SUPP_RATES pExtSupportRateIEs
)
{
BYTE byMaxAckRate;
BYTE byBasicRate;
UINT ii;
if (byRxDataRate <= RATE_11M) {
byMaxAckRate = RATE_1M;
} else {
// 24M is mandatory for 802.11a and 802.11g
byMaxAckRate = RATE_24M;
}
if (pSupportRateIEs) {
for (ii = 0; ii < pSupportRateIEs->len; ii++) {
if (pSupportRateIEs->abyRates[ii] & 0x80) {
byBasicRate = DATARATEbyGetRateIdx(pSupportRateIEs->abyRates[ii]);
if ((byBasicRate <= byRxDataRate) &&
(byBasicRate > byMaxAckRate)) {
byMaxAckRate = byBasicRate;
}
}
}
}
if (pExtSupportRateIEs) {
for (ii = 0; ii < pExtSupportRateIEs->len; ii++) {
if (pExtSupportRateIEs->abyRates[ii] & 0x80) {
byBasicRate = DATARATEbyGetRateIdx(pExtSupportRateIEs->abyRates[ii]);
if ((byBasicRate <= byRxDataRate) &&
(byBasicRate > byMaxAckRate)) {
byMaxAckRate = byBasicRate;
}
}
}
}
return byMaxAckRate;
}
/*+
*
* Description:
* Set Authentication Mode
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* eAuthMode - Authentication mode
* Out:
* none
*
* Return Value: none
*
-*/
void
VNTWIFIvSetAuthenticationMode (
void *pMgmtHandle,
WMAC_AUTHENTICATION_MODE eAuthMode
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
pMgmt->eAuthenMode = eAuthMode;
if ((eAuthMode == WMAC_AUTH_SHAREKEY) ||
(eAuthMode == WMAC_AUTH_AUTO)) {
pMgmt->bShareKeyAlgorithm = TRUE;
} else {
pMgmt->bShareKeyAlgorithm = FALSE;
}
}
/*+
*
* Description:
* Set Encryption Mode
*
* Parameters:
* In:
* pMgmtHandle - pointer to management object
* eAuthMode - Authentication mode
* Out:
* none
*
* Return Value: none
*
-*/
void
VNTWIFIvSetEncryptionMode (
void *pMgmtHandle,
WMAC_ENCRYPTION_MODE eEncryptionMode
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
pMgmt->eEncryptionMode = eEncryptionMode;
if ((eEncryptionMode == WMAC_ENCRYPTION_WEPEnabled) ||
(eEncryptionMode == WMAC_ENCRYPTION_TKIPEnabled) ||
(eEncryptionMode == WMAC_ENCRYPTION_AESEnabled) ) {
pMgmt->bPrivacyInvoked = TRUE;
} else {
pMgmt->bPrivacyInvoked = FALSE;
}
}
BOOL
VNTWIFIbConfigPhyMode (
void *pMgmtHandle,
CARD_PHY_TYPE ePhyType
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
if ((ePhyType != PHY_TYPE_AUTO) &&
(ePhyType != pMgmt->eCurrentPHYMode)) {
if (CARDbSetPhyParameter(pMgmt->pAdapter, ePhyType, 0, 0, NULL, NULL)==TRUE) {
pMgmt->eCurrentPHYMode = ePhyType;
} else {
return(FALSE);
}
}
pMgmt->eConfigPHYMode = ePhyType;
return(TRUE);
}
void
VNTWIFIbGetConfigPhyMode (
void *pMgmtHandle,
void *pePhyType
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
if ((pMgmt != NULL) && (pePhyType != NULL)) {
*(PCARD_PHY_TYPE)pePhyType = pMgmt->eConfigPHYMode;
}
}
/*+
*
* Description:
* Clear BSS List Database except current assoc BSS
*
* Parameters:
* In:
* pMgmtHandle - Management Object structure
* bLinkPass - Current Link status
* Out:
*
* Return Value: None.
*
-*/
/*+
*
* Description:
* Query BSS List in management database
*
* Parameters:
* In:
* pMgmtHandle - Management Object structure
* Out:
* puBSSCount - BSS count
* pvFirstBSS - pointer to first BSS
*
* Return Value: None.
*
-*/
void
VNTWIFIvQueryBSSList (
void *pMgmtHandle,
PUINT puBSSCount,
void **pvFirstBSS
)
{
UINT ii = 0;
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
PKnownBSS pBSS = NULL;
UINT uCount = 0;
*pvFirstBSS = NULL;
for (ii = 0; ii < MAX_BSS_NUM; ii++) {
pBSS = &(pMgmt->sBSSList[ii]);
if (!pBSS->bActive) {
continue;
}
if (*pvFirstBSS == NULL) {
*pvFirstBSS = &(pMgmt->sBSSList[ii]);
}
uCount++;
}
*puBSSCount = uCount;
}
void
VNTWIFIvGetNextBSS (
void *pMgmtHandle,
void *pvCurrentBSS,
void **pvNextBSS
)
{
PKnownBSS pBSS = (PKnownBSS) pvCurrentBSS;
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
*pvNextBSS = NULL;
while (*pvNextBSS == NULL) {
pBSS++;
if (pBSS > &(pMgmt->sBSSList[MAX_BSS_NUM])) {
return;
}
if (pBSS->bActive == TRUE) {
*pvNextBSS = pBSS;
return;
}
}
}
/*+
*
* Description:
* Update Tx attemps, Tx failure counter in Node DB
*
* In:
* Out:
* none
*
* Return Value: none
*
-*/
void
VNTWIFIvUpdateNodeTxCounter(
void *pMgmtHandle,
PBYTE pbyDestAddress,
BOOL bTxOk,
WORD wRate,
PBYTE pbyTxFailCount
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
UINT uNodeIndex = 0;
UINT ii;
if ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) ||
(pMgmt->eCurrMode == WMAC_MODE_ESS_AP)) {
if (BSSDBbIsSTAInNodeDB(pMgmt, pbyDestAddress, &uNodeIndex) == FALSE) {
return;
}
}
pMgmt->sNodeDBTable[uNodeIndex].uTxAttempts++;
if (bTxOk == TRUE) {
// transmit success, TxAttempts at least plus one
pMgmt->sNodeDBTable[uNodeIndex].uTxOk[MAX_RATE]++;
pMgmt->sNodeDBTable[uNodeIndex].uTxOk[wRate]++;
} else {
pMgmt->sNodeDBTable[uNodeIndex].uTxFailures++;
}
pMgmt->sNodeDBTable[uNodeIndex].uTxRetry += pbyTxFailCount[MAX_RATE];
for(ii=0;ii<MAX_RATE;ii++) {
pMgmt->sNodeDBTable[uNodeIndex].uTxFail[ii] += pbyTxFailCount[ii];
}
return;
}
void
VNTWIFIvGetTxRate(
void *pMgmtHandle,
PBYTE pbyDestAddress,
PWORD pwTxDataRate,
PBYTE pbyACKRate,
PBYTE pbyCCKBasicRate,
PBYTE pbyOFDMBasicRate
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
UINT uNodeIndex = 0;
WORD wTxDataRate = RATE_1M;
BYTE byACKRate = RATE_1M;
BYTE byCCKBasicRate = RATE_1M;
BYTE byOFDMBasicRate = RATE_24M;
PWLAN_IE_SUPP_RATES pSupportRateIEs = NULL;
PWLAN_IE_SUPP_RATES pExtSupportRateIEs = NULL;
if ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) ||
(pMgmt->eCurrMode == WMAC_MODE_ESS_AP)) {
// Adhoc Tx rate decided from node DB
if(BSSDBbIsSTAInNodeDB(pMgmt, pbyDestAddress, &uNodeIndex)) {
wTxDataRate = (pMgmt->sNodeDBTable[uNodeIndex].wTxDataRate);
pSupportRateIEs = (PWLAN_IE_SUPP_RATES) (pMgmt->sNodeDBTable[uNodeIndex].abyCurrSuppRates);
pExtSupportRateIEs = (PWLAN_IE_SUPP_RATES) (pMgmt->sNodeDBTable[uNodeIndex].abyCurrExtSuppRates);
} else {
if (pMgmt->eCurrentPHYMode != PHY_TYPE_11A) {
wTxDataRate = RATE_2M;
} else {
wTxDataRate = RATE_24M;
}
pSupportRateIEs = (PWLAN_IE_SUPP_RATES) pMgmt->abyCurrSuppRates;
pExtSupportRateIEs = (PWLAN_IE_SUPP_RATES) pMgmt->abyCurrExtSuppRates;
}
} else { // Infrastructure: rate decided from AP Node, index = 0
wTxDataRate = (pMgmt->sNodeDBTable[0].wTxDataRate);
#ifdef PLICE_DEBUG
printk("GetTxRate:AP MAC is %02x:%02x:%02x:%02x:%02x:%02x,TxRate is %d\n",
pMgmt->sNodeDBTable[0].abyMACAddr[0],pMgmt->sNodeDBTable[0].abyMACAddr[1],
pMgmt->sNodeDBTable[0].abyMACAddr[2],pMgmt->sNodeDBTable[0].abyMACAddr[3],
pMgmt->sNodeDBTable[0].abyMACAddr[4],pMgmt->sNodeDBTable[0].abyMACAddr[5],wTxDataRate);
#endif
pSupportRateIEs = (PWLAN_IE_SUPP_RATES) pMgmt->abyCurrSuppRates;
pExtSupportRateIEs = (PWLAN_IE_SUPP_RATES) pMgmt->abyCurrExtSuppRates;
}
byACKRate = VNTWIFIbyGetACKTxRate( (BYTE) wTxDataRate,
pSupportRateIEs,
pExtSupportRateIEs
);
if (byACKRate > (BYTE) wTxDataRate) {
byACKRate = (BYTE) wTxDataRate;
}
byCCKBasicRate = VNTWIFIbyGetACKTxRate( RATE_11M,
pSupportRateIEs,
pExtSupportRateIEs
);
byOFDMBasicRate = VNTWIFIbyGetACKTxRate(RATE_54M,
pSupportRateIEs,
pExtSupportRateIEs
);
*pwTxDataRate = wTxDataRate;
*pbyACKRate = byACKRate;
*pbyCCKBasicRate = byCCKBasicRate;
*pbyOFDMBasicRate = byOFDMBasicRate;
return;
}
BYTE
VNTWIFIbyGetKeyCypher(
void *pMgmtHandle,
BOOL bGroupKey
)
{
PSMgmtObject pMgmt = (PSMgmtObject)pMgmtHandle;
if (bGroupKey == TRUE) {
return (pMgmt->byCSSGK);
} else {
return (pMgmt->byCSSPK);
}
}
/*
BOOL
VNTWIFIbInit(
void *pAdapterHandler,
void **pMgmtHandler
)
{
PSMgmtObject pMgmt = NULL;
UINT ii;
pMgmt = (PSMgmtObject)kmalloc(sizeof(SMgmtObject), (int)GFP_ATOMIC);
if (pMgmt == NULL) {
*pMgmtHandler = NULL;
return FALSE;
}
memset(pMgmt, 0, sizeof(SMgmtObject));
pMgmt->pAdapter = (void *) pAdapterHandler;
// should initial MAC address abyMACAddr
for(ii=0;ii<WLAN_BSSID_LEN;ii++) {
pMgmt->abyDesireBSSID[ii] = 0xFF;
}
pMgmt->pbyPSPacketPool = &pMgmt->byPSPacketPool[0];
pMgmt->pbyMgmtPacketPool = &pMgmt->byMgmtPacketPool[0];
pMgmt->byCSSPK = KEY_CTL_NONE;
pMgmt->byCSSGK = KEY_CTL_NONE;
pMgmt->wIBSSBeaconPeriod = DEFAULT_IBSS_BI;
pMgmt->cbFreeCmdQueue = CMD_Q_SIZE;
pMgmt->uCmdDequeueIdx = 0;
pMgmt->uCmdEnqueueIdx = 0;
pMgmt->eCommandState = WLAN_CMD_STATE_IDLE;
pMgmt->bCmdStop = FALSE;
pMgmt->bCmdRunning = FALSE;
*pMgmtHandler = pMgmt;
return TRUE;
}
*/
BOOL
VNTWIFIbSetPMKIDCache (
void *pMgmtObject,
ULONG ulCount,
void *pPMKIDInfo
)
{
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
if (ulCount > MAX_PMKID_CACHE) {
return (FALSE);
}
pMgmt->gsPMKIDCache.BSSIDInfoCount = ulCount;
memcpy(pMgmt->gsPMKIDCache.BSSIDInfo, pPMKIDInfo, (ulCount*sizeof(PMKIDInfo)));
return (TRUE);
}
WORD
VNTWIFIwGetMaxSupportRate(
void *pMgmtObject
)
{
WORD wRate = RATE_54M;
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
for(wRate = RATE_54M; wRate > RATE_1M; wRate--) {
if (pMgmt->sNodeDBTable[0].wSuppRate & (1<<wRate)) {
return (wRate);
}
}
if (pMgmt->eCurrentPHYMode == PHY_TYPE_11A) {
return (RATE_6M);
} else {
return (RATE_1M);
}
}
void
VNTWIFIvSet11h (
void *pMgmtObject,
BOOL b11hEnable
)
{
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
pMgmt->b11hEnable = b11hEnable;
}
BOOL
VNTWIFIbMeasureReport(
void *pMgmtObject,
BOOL bEndOfReport,
void *pvMeasureEID,
BYTE byReportMode,
BYTE byBasicMap,
BYTE byCCAFraction,
PBYTE pbyRPIs
)
{
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
PBYTE pbyCurrentEID = (PBYTE) (pMgmt->pCurrMeasureEIDRep);
//spin_lock_irq(&pDevice->lock);
if ((pvMeasureEID != NULL) &&
(pMgmt->uLengthOfRepEIDs < (WLAN_A3FR_MAXLEN - sizeof(MEASEURE_REP) - sizeof(WLAN_80211HDR_A3) - 3))
) {
pMgmt->pCurrMeasureEIDRep->byElementID = WLAN_EID_MEASURE_REP;
pMgmt->pCurrMeasureEIDRep->len = 3;
pMgmt->pCurrMeasureEIDRep->byToken = ((PWLAN_IE_MEASURE_REQ) pvMeasureEID)->byToken;
pMgmt->pCurrMeasureEIDRep->byMode = byReportMode;
pMgmt->pCurrMeasureEIDRep->byType = ((PWLAN_IE_MEASURE_REQ) pvMeasureEID)->byType;
switch (pMgmt->pCurrMeasureEIDRep->byType) {
case MEASURE_TYPE_BASIC :
pMgmt->pCurrMeasureEIDRep->len += sizeof(MEASEURE_REP_BASIC);
memcpy( &(pMgmt->pCurrMeasureEIDRep->sRep.sBasic),
&(((PWLAN_IE_MEASURE_REQ) pvMeasureEID)->sReq),
sizeof(MEASEURE_REQ));
pMgmt->pCurrMeasureEIDRep->sRep.sBasic.byMap = byBasicMap;
break;
case MEASURE_TYPE_CCA :
pMgmt->pCurrMeasureEIDRep->len += sizeof(MEASEURE_REP_CCA);
memcpy( &(pMgmt->pCurrMeasureEIDRep->sRep.sCCA),
&(((PWLAN_IE_MEASURE_REQ) pvMeasureEID)->sReq),
sizeof(MEASEURE_REQ));
pMgmt->pCurrMeasureEIDRep->sRep.sCCA.byCCABusyFraction = byCCAFraction;
break;
case MEASURE_TYPE_RPI :
pMgmt->pCurrMeasureEIDRep->len += sizeof(MEASEURE_REP_RPI);
memcpy( &(pMgmt->pCurrMeasureEIDRep->sRep.sRPI),
&(((PWLAN_IE_MEASURE_REQ) pvMeasureEID)->sReq),
sizeof(MEASEURE_REQ));
memcpy(pMgmt->pCurrMeasureEIDRep->sRep.sRPI.abyRPIdensity, pbyRPIs, 8);
break;
default :
break;
}
pbyCurrentEID += (2 + pMgmt->pCurrMeasureEIDRep->len);
pMgmt->uLengthOfRepEIDs += (2 + pMgmt->pCurrMeasureEIDRep->len);
pMgmt->pCurrMeasureEIDRep = (PWLAN_IE_MEASURE_REP) pbyCurrentEID;
}
if (bEndOfReport == TRUE) {
IEEE11hbMSRRepTx(pMgmt);
}
//spin_unlock_irq(&pDevice->lock);
return (TRUE);
}
BOOL
VNTWIFIbChannelSwitch(
void *pMgmtObject,
BYTE byNewChannel
)
{
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
//spin_lock_irq(&pDevice->lock);
pMgmt->uCurrChannel = byNewChannel;
pMgmt->bSwitchChannel = FALSE;
//spin_unlock_irq(&pDevice->lock);
return TRUE;
}
/*
BOOL
VNTWIFIbRadarPresent(
void *pMgmtObject,
BYTE byChannel
)
{
PSMgmtObject pMgmt = (PSMgmtObject) pMgmtObject;
if ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) &&
(byChannel == (BYTE) pMgmt->uCurrChannel) &&
(pMgmt->bSwitchChannel != TRUE) &&
(pMgmt->b11hEnable == TRUE)) {
if (IS_ETH_ADDRESS_EQUAL(pMgmt->abyIBSSDFSOwner, CARDpGetCurrentAddress(pMgmt->pAdapter))) {
pMgmt->byNewChannel = CARDbyAutoChannelSelect(pMgmt->pAdapter,(BYTE) pMgmt->uCurrChannel);
pMgmt->bSwitchChannel = TRUE;
}
BEACONbSendBeacon(pMgmt);
CARDbChannelSwitch(pMgmt->pAdapter, 0, pMgmt->byNewChannel, 10);
}
return TRUE;
}
*/