/*******************************************************************************
 * Agere Systems Inc.
 * Wireless device driver for Linux (wlags49).
 *
 * Copyright (c) 1998-2003 Agere Systems Inc.
 * All rights reserved.
 *   http://www.agere.com
 *
 * Initially developed by TriplePoint, Inc.
 *   http://www.triplepoint.com
 *
 *------------------------------------------------------------------------------
 *
 *   This file defines handling routines for the private IOCTLs
 *
 *------------------------------------------------------------------------------
 *
 * 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 © 2003 Agere Systems Inc.
 * 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.
 *
 ******************************************************************************/




/*******************************************************************************
 * VERSION CONTROL INFORMATION
 *******************************************************************************
 *
 * $Author: nico $
 * $Date: 2004/08/03 11:39:39 $
 * $Revision: 1.5 $
 * $Source: /usr/local/cvs/wl_lkm/wireless/wl_priv.c,v $
 *
 ******************************************************************************/




/*******************************************************************************
 * include files
 ******************************************************************************/
#include <wireless/wl_version.h>

#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/uaccess.h>

#include <hcf/debug.h>
#include <hcf.h>
#include <hcfdef.h>

#include <wireless/wl_if.h>
#include <wireless/wl_internal.h>
#include <wireless/wl_enc.h>
#include <wireless/wl_main.h>
#include <wireless/wl_priv.h>
#include <wireless/wl_util.h>
#include <wireless/wl_netdev.h>

int wvlan_uil_connect( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_disconnect( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_action( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_block( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_unblock( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_send_diag_msg( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_put_info( struct uilreq *urq, struct wl_private *lp );
int wvlan_uil_get_info( struct uilreq *urq, struct wl_private *lp );

int cfg_driver_info( struct uilreq *urq, struct wl_private *lp );
int cfg_driver_identity( struct uilreq *urq, struct wl_private *lp );


/*******************************************************************************
 * global variables
 ******************************************************************************/
#if DBG
extern dbg_info_t *DbgInfo;
#endif  // DBG




/* If USE_UIL is not defined, then none of the UIL Interface code below will
   be included in the build */
#ifdef USE_UIL

/*******************************************************************************
 *	wvlan_uil()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler function for the UIL interface.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_uil( struct uilreq *urq, struct wl_private *lp )
{
	int ioctl_ret = 0;
	/*------------------------------------------------------------------------*/

	DBG_FUNC( "wvlan_uil" );
	DBG_ENTER( DbgInfo );

	switch( urq->command ) {
	  case UIL_FUN_CONNECT:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_CONNECT\n");
		ioctl_ret = wvlan_uil_connect( urq, lp );
		break;
	  case UIL_FUN_DISCONNECT:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_DISCONNECT\n");
		ioctl_ret = wvlan_uil_disconnect( urq, lp );
		break;
	  case UIL_FUN_ACTION:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_ACTION\n" );
		ioctl_ret = wvlan_uil_action( urq, lp );
		break;
	  case UIL_FUN_SEND_DIAG_MSG:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_SEND_DIAG_MSG\n");
		ioctl_ret = wvlan_uil_send_diag_msg( urq, lp );
		break;
	  case UIL_FUN_GET_INFO:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_GET_INFO\n");
		ioctl_ret = wvlan_uil_get_info( urq, lp );
		break;
	  case UIL_FUN_PUT_INFO:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- WVLAN2_UIL_PUT_INFO\n");
		ioctl_ret = wvlan_uil_put_info( urq, lp );
		break;
	default:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL -- UNSUPPORTED UIL CODE: 0x%X", urq->command );
		ioctl_ret = -EOPNOTSUPP;
		break;
	}
	DBG_LEAVE( DbgInfo );
	return ioctl_ret;
} // wvlan_uil
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_connect()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Connect to the UIL in order to make a request.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_connect( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_connect" );
	DBG_ENTER( DbgInfo );


	if( !( lp->flags & WVLAN2_UIL_CONNECTED )) {
		lp->flags |= WVLAN2_UIL_CONNECTED;
		urq->hcfCtx = &( lp->hcfCtx );
		urq->result = UIL_SUCCESS;
	} else {
		DBG_WARNING( DbgInfo, "UIL_ERR_IN_USE\n" );
		urq->result = UIL_ERR_IN_USE;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_connect
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_disconnect()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Disonnect from the UIL after a request has been completed.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_disconnect( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_disconnect" );
	DBG_ENTER( DbgInfo );


	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if (lp->flags & WVLAN2_UIL_CONNECTED) {
			lp->flags &= ~WVLAN2_UIL_CONNECTED;
			/*
			if (lp->flags & WVLAN2_UIL_BUSY) {
				lp->flags &= ~WVLAN2_UIL_BUSY;
				netif_start_queue(lp->dev);
			}
			*/
		}

		urq->hcfCtx = NULL;
		urq->result = UIL_SUCCESS;
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_disconnect
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_action()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Handler for the UIL_ACT_xxx subcodes associated with UIL_FUN_ACTION
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_action( struct uilreq *urq, struct wl_private *lp )
{
	int     result = 0;
	ltv_t   *ltv;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_action" );
	DBG_ENTER( DbgInfo );


	if( urq->hcfCtx == &( lp->hcfCtx )) {
		/* Make sure there's an LTV in the request buffer */
		ltv = (ltv_t *)urq->data;
		if( ltv != NULL ) {
			/* Switch on the Type field of the LTV contained in the request
			   buffer */
			switch( ltv->typ ) {
			case UIL_ACT_BLOCK:
				DBG_TRACE( DbgInfo, "UIL_ACT_BLOCK\n" );
				result = wvlan_uil_block( urq, lp );
				break;
			case UIL_ACT_UNBLOCK:
				DBG_TRACE( DbgInfo, "UIL_ACT_UNBLOCK\n" );
				result = wvlan_uil_unblock( urq, lp );
				break;
			case UIL_ACT_SCAN:
				DBG_TRACE( DbgInfo, "UIL_ACT_SCAN\n" );
				urq->result = hcf_action( &( lp->hcfCtx ), MDD_ACT_SCAN );
				break;
			case UIL_ACT_APPLY:
				DBG_TRACE( DbgInfo, "UIL_ACT_APPLY\n" );
				urq->result = wl_apply( lp );
				break;
			case UIL_ACT_RESET:
				DBG_TRACE( DbgInfo, "UIL_ACT_RESET\n" );
				urq->result = wl_go( lp );
				break;
			default:
				DBG_WARNING( DbgInfo, "Unknown action code: 0x%x\n", ltv->typ );
				break;
			}
		} else {
			DBG_ERROR( DbgInfo, "Bad LTV for this action\n" );
			urq->result = UIL_ERR_LEN;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_action
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_block()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sets a block in the driver to prevent access to the card by other
 *  processes.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/

int wvlan_uil_block( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_block" );
	DBG_ENTER( DbgInfo );

	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if( capable( CAP_NET_ADMIN )) {
			lp->flags |= WVLAN2_UIL_BUSY;
			netif_stop_queue(lp->dev);
			WL_WDS_NETIF_STOP_QUEUE( lp );
			urq->result = UIL_SUCCESS;
		} else {
			DBG_ERROR( DbgInfo, "EPERM\n" );
			urq->result = UIL_FAILURE;
			result = -EPERM;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_block
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_unblock()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Unblocks the driver to restore access to the card by other processes.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_unblock( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_unblock" );
	DBG_ENTER( DbgInfo );

	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if( capable( CAP_NET_ADMIN )) {
			if (lp->flags & WVLAN2_UIL_BUSY) {
				lp->flags &= ~WVLAN2_UIL_BUSY;
				netif_wake_queue(lp->dev);
				WL_WDS_NETIF_WAKE_QUEUE( lp );
			}
		} else {
			DBG_ERROR( DbgInfo, "EPERM\n" );
			urq->result = UIL_FAILURE;
			result = -EPERM;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_unblock
/*============================================================================*/




/*******************************************************************************
 *	wvlan_uil_send_diag_msg()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sends a diagnostic message to the card.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_send_diag_msg( struct uilreq *urq, struct wl_private *lp )
{
	int         result = 0;
	DESC_STRCT  Descp[1];
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_uil_send_diag_msg" );
	DBG_ENTER( DbgInfo );

	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if( capable( CAP_NET_ADMIN )) {
			if ((urq->data != NULL) && (urq->len != 0)) {
				if (lp->hcfCtx.IFB_RscInd != 0) {
					u_char *data;

					// Verify the user buffer
					result = verify_area(VERIFY_READ, urq->data, urq->len);
					if (result != 0) {
						DBG_ERROR( DbgInfo, "verify_area failed, result: %d\n", result );
						urq->result = UIL_FAILURE;
						DBG_LEAVE( DbgInfo );
						return result;
					}

					if ((data = kmalloc(urq->len, GFP_KERNEL)) != NULL) {
						memset( Descp, 0, sizeof( DESC_STRCT ));
						memcpy( data, urq->data, urq->len );

						Descp[0].buf_addr       = (wci_bufp)data;
						Descp[0].BUF_CNT        = urq->len;
						Descp[0].next_desc_addr = 0;    // terminate list

						hcf_send_msg( &(lp->hcfCtx),  &Descp[0], HCF_PORT_0 );
						kfree( data );
					} else {
						DBG_ERROR( DbgInfo, "ENOMEM\n" );
						urq->result = UIL_FAILURE;
						result = -ENOMEM;
						DBG_LEAVE( DbgInfo );
						return result;
					}

				} else {
					urq->result = UIL_ERR_BUSY;
				}

			} else {
				urq->result = UIL_FAILURE;
			}
		} else {
			DBG_ERROR( DbgInfo, "EPERM\n" );
			urq->result = UIL_FAILURE;
			result = -EPERM;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_send_diag_msg
/*============================================================================*/


/*******************************************************************************
 *	wvlan_uil_put_info()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sends a specific RID directly to the driver to set configuration info.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_put_info( struct uilreq *urq, struct wl_private *lp )
{
	int                     result = 0;
	ltv_t                   *pLtv;
	bool_t                  ltvAllocated = FALSE;
	ENCSTRCT                sEncryption;

#ifdef USE_WDS
	hcf_16                  hcfPort  = HCF_PORT_0;
#endif  /* USE_WDS */
	/*------------------------------------------------------------------------*/
	DBG_FUNC( "wvlan_uil_put_info" );
	DBG_ENTER( DbgInfo );


	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if( capable( CAP_NET_ADMIN )) {
			if(( urq->data != NULL ) && ( urq->len != 0 )) {
				/* Make sure that we have at least a command and length to send. */
				if( urq->len < ( sizeof( hcf_16 ) * 2 )) {
					urq->len = sizeof( lp->ltvRecord );
					urq->result = UIL_ERR_LEN;
					DBG_ERROR( DbgInfo, "No Length/Type in LTV!!!\n" );
					DBG_ERROR( DbgInfo, "UIL_ERR_LEN\n" );
					DBG_LEAVE( DbgInfo );
					return result;
				}

				/* Verify the user buffer */
				result = verify_area( VERIFY_READ, urq->data, urq->len );
				if( result != 0 ) {
					urq->result = UIL_FAILURE;
					DBG_ERROR( DbgInfo, "verify_area(), VERIFY_READ FAILED\n" );
					DBG_LEAVE( DbgInfo );
					return result;
				}

				/* Get only the command and length information. */
				copy_from_user( &( lp->ltvRecord ), urq->data, sizeof( hcf_16 ) * 2 );

				/* Make sure the incoming LTV record length is within the bounds of the
				   IOCTL length */
				if((( lp->ltvRecord.len + 1 ) * sizeof( hcf_16 )) > urq->len ) {
					urq->len = sizeof( lp->ltvRecord );
					urq->result = UIL_ERR_LEN;
					DBG_ERROR( DbgInfo, "UIL_ERR_LEN\n" );
					DBG_LEAVE( DbgInfo );
					return result;
				}

				/* If the requested length is greater than the size of our local
				   LTV record, try to allocate it from the kernel stack.
				   Otherwise, we just use our local LTV record. */
				if( urq->len > sizeof( lp->ltvRecord )) {
					if(( pLtv = (ltv_t *)kmalloc( urq->len, GFP_KERNEL )) != NULL ) {
						ltvAllocated = TRUE;
					} else {
						DBG_ERROR( DbgInfo, "Alloc FAILED\n" );
						urq->len = sizeof( lp->ltvRecord );
						urq->result = UIL_ERR_LEN;
						result = -ENOMEM;
						DBG_LEAVE( DbgInfo );
						return result;
					}
				} else {
					pLtv = &( lp->ltvRecord );
				}

				/* Copy the data from the user's buffer into the local LTV
				   record data area. */
				copy_from_user( pLtv, urq->data, urq->len );


				/* We need to snoop the commands to see if there is anything we
				   need to store for the purposes of a reset or start/stop
				   sequence. Perform endian translation as needed */
				switch( pLtv->typ ) {
				case CFG_CNF_PORT_TYPE:
					lp->PortType    = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_OWN_MAC_ADDR:
					/* TODO: determine if we are going to store anything based on this */
					break;
				case CFG_CNF_OWN_CHANNEL:
					lp->Channel     = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				/* CFG_CNF_OWN_SSID currently same as CNF_DESIRED_SSID. Do we
				   need seperate storage for this? */
				//case CFG_CNF_OWN_SSID:
				case CFG_CNF_OWN_ATIM_WINDOW:
					lp->atimWindow  = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_SYSTEM_SCALE:
					lp->DistanceBetweenAPs  = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );

				case CFG_CNF_MAX_DATA_LEN:
					/* TODO: determine if we are going to store anything based
					   on this */
					break;
				case CFG_CNF_PM_ENABLED:
					lp->PMEnabled   = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_MCAST_RX:
					lp->MulticastReceive    = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_MAX_SLEEP_DURATION:
					lp->MaxSleepDuration    = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_HOLDOVER_DURATION:
					lp->holdoverDuration    = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_OWN_NAME:
					memset( lp->StationName, 0, sizeof( lp->StationName ));
					memcpy( (void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
					pLtv->u.u16[0] = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_LOAD_BALANCING:
					lp->loadBalancing       = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_MEDIUM_DISTRIBUTION:
					lp->mediumDistribution  = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#ifdef WARP 
				case CFG_CNF_TX_POW_LVL:
					lp->txPowLevel          = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				//case CFG_CNF_SHORT_RETRY_LIMIT:    // Short Retry Limit
				//case 0xFC33:    // Long Retry Limit
				case CFG_SUPPORTED_RATE_SET_CNTL:        // Supported Rate Set Control
					lp->srsc[0]             = pLtv->u.u16[0];
					lp->srsc[1]             = pLtv->u.u16[1];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					pLtv->u.u16[1]          = CNV_INT_TO_LITTLE( pLtv->u.u16[1] );
					break;
				case CFG_BASIC_RATE_SET_CNTL:        // Basic Rate Set Control
					lp->brsc[0]             = pLtv->u.u16[0];
					lp->brsc[1]             = pLtv->u.u16[1];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					pLtv->u.u16[1]          = CNV_INT_TO_LITTLE( pLtv->u.u16[1] );
					break;
				case CFG_CNF_CONNECTION_CNTL:
					lp->connectionControl   = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				//case CFG_PROBE_DATA_RATE:
#endif  // HERMES25

#if 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint

				case CFG_CNF_OWN_DTIM_PERIOD:
					lp->DTIMPeriod  = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#ifdef WARP 
				case CFG_CNF_OWN_BEACON_INTERVAL:        // Own Beacon Interval
					lp->ownBeaconInterval   = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#endif // WARP
				case CFG_COEXISTENSE_BEHAVIOUR:         // Coexistence behavior
					lp->coexistence         = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#ifdef USE_WDS
				case CFG_CNF_WDS_ADDR1:
					memcpy( &lp->wds_port[0].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_1;
					break;
				case CFG_CNF_WDS_ADDR2:
					memcpy( &lp->wds_port[1].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_2;
					break;
				case CFG_CNF_WDS_ADDR3:
					memcpy( &lp->wds_port[2].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_3;
					break;
				case CFG_CNF_WDS_ADDR4:
					memcpy( &lp->wds_port[3].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_4;
					break;
				case CFG_CNF_WDS_ADDR5:
					memcpy( &lp->wds_port[4].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_5;
					break;
				case CFG_CNF_WDS_ADDR6:
					memcpy( &lp->wds_port[5].wdsAddress, &pLtv->u.u8[0], ETH_ALEN );
					hcfPort = HCF_PORT_6;
					break;
#endif  /* USE_WDS */

				case CFG_CNF_MCAST_PM_BUF:
					lp->multicastPMBuffering    = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_REJECT_ANY:
					lp->RejectAny   = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#endif

				case CFG_CNF_ENCRYPTION:
					lp->EnableEncryption    = pLtv->u.u16[0];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_CNF_AUTHENTICATION:
					lp->authentication  = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#if 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint

				//case CFG_CNF_EXCL_UNENCRYPTED:
					//lp->ExcludeUnencrypted  = pLtv->u.u16[0];
					//pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					//break;
				case CFG_CNF_MCAST_RATE:
					/* TODO: determine if we are going to store anything based on this */
					break;
				case CFG_CNF_INTRA_BSS_RELAY:
					lp->intraBSSRelay   = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#endif

				case CFG_CNF_MICRO_WAVE:
					/* TODO: determine if we are going to store anything based on this */
					break;
				//case CFG_CNF_LOAD_BALANCING:
					/* TODO: determine if we are going to store anything based on this */
					//break;
				//case CFG_CNF_MEDIUM_DISTRIBUTION:
					/* TODO: determine if we are going to store anything based on this */
					//break;
				//case CFG_CNF_RX_ALL_GROUP_ADDRESS:
					// TODO: determine if we are going to store anything based on this
					//break;
				//case CFG_CNF_COUNTRY_INFO:
					/* TODO: determine if we are going to store anything based on this */
					//break;
				case CFG_CNF_OWN_SSID:
				//case CNF_DESIRED_SSID:
				case CFG_DESIRED_SSID:
					memset( lp->NetworkName, 0, sizeof( lp->NetworkName ));
					memcpy( (void *)lp->NetworkName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0] );
					pLtv->u.u16[0] = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );

					/* take care of the special network name "ANY" case */
					if(( strlen( &pLtv->u.u8[2]        ) == 0 ) ||
					   ( strcmp( &pLtv->u.u8[2], "ANY" ) == 0 ) ||
					   ( strcmp( &pLtv->u.u8[2], "any" ) == 0 )) {
						/* set the SSID_STRCT llen field (u16[0]) to zero, and the
						effectually null the string u8[2] */
						pLtv->u.u16[0] = 0;
						pLtv->u.u8[2]  = 0;
					}
					break;
				case CFG_GROUP_ADDR:
					/* TODO: determine if we are going to store anything based on this */
					break;
				case CFG_CREATE_IBSS:
					lp->CreateIBSS  = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_RTS_THRH:
					lp->RTSThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_TX_RATE_CNTL:
					lp->TxRateControl[0]    = pLtv->u.u16[0];
					lp->TxRateControl[1]    = pLtv->u.u16[1];
					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					pLtv->u.u16[1]          = CNV_INT_TO_LITTLE( pLtv->u.u16[1] );
					break;
				case CFG_PROMISCUOUS_MODE:
					/* TODO: determine if we are going to store anything based on this */
					break;
				//case CFG_WAKE_ON_LAN:
					/* TODO: determine if we are going to store anything based on this */
					//break;
#if 1 //;? #if (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint
				case CFG_RTS_THRH0:
					lp->RTSThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_TX_RATE_CNTL0:
//;?no idea what this should be, get going so comment it out					lp->TxRateControl   = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
#ifdef USE_WDS
				case CFG_RTS_THRH1:
					lp->wds_port[0].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_1;
					break;
				case CFG_RTS_THRH2:
					lp->wds_port[1].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_2;
					break;
				case CFG_RTS_THRH3:
					lp->wds_port[2].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_3;
					break;
				case CFG_RTS_THRH4:
					lp->wds_port[3].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_4;
					break;
				case CFG_RTS_THRH5:
					lp->wds_port[4].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_5;
					break;
				case CFG_RTS_THRH6:
					lp->wds_port[5].rtsThreshold    = pLtv->u.u16[0];
					pLtv->u.u16[0]                  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                         = HCF_PORT_6;
					break;
				case CFG_TX_RATE_CNTL1:
					lp->wds_port[0].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_1;
					break;
				case CFG_TX_RATE_CNTL2:
					lp->wds_port[1].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_2;
					break;
				case CFG_TX_RATE_CNTL3:
					lp->wds_port[2].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_3;
					break;
				case CFG_TX_RATE_CNTL4:
					lp->wds_port[3].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_4;
					break;
				case CFG_TX_RATE_CNTL5:
					lp->wds_port[4].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_5;
					break;
				case CFG_TX_RATE_CNTL6:
					lp->wds_port[5].txRateCntl  = pLtv->u.u16[0];
					pLtv->u.u16[0]              = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					hcfPort                     = HCF_PORT_6;
					break;
#endif  /* USE_WDS */
#endif  /* (HCF_TYPE) & HCF_TYPE_AP */

				case CFG_DEFAULT_KEYS:
					{
						CFG_DEFAULT_KEYS_STRCT *pKeys = (CFG_DEFAULT_KEYS_STRCT *)pLtv;

						pKeys->key[0].len = CNV_INT_TO_LITTLE( pKeys->key[0].len );
						pKeys->key[1].len = CNV_INT_TO_LITTLE( pKeys->key[1].len );
						pKeys->key[2].len = CNV_INT_TO_LITTLE( pKeys->key[2].len );
						pKeys->key[3].len = CNV_INT_TO_LITTLE( pKeys->key[3].len );

						memcpy( (void *)&(lp->DefaultKeys), (void *)pKeys,
								sizeof( CFG_DEFAULT_KEYS_STRCT ));
					}
					break;
				case CFG_TX_KEY_ID:
					lp->TransmitKeyID   = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_SCAN_SSID:
					/* TODO: determine if we are going to store anything based on this */
					break;
				case CFG_TICK_TIME:
					/* TODO: determine if we are going to store anything based on this */
					break;
				/* these RIDS are Info RIDs, and should they be allowed for puts??? */
				case CFG_MAX_LOAD_TIME:
				case CFG_DL_BUF:
				//case CFG_HSI_SUP_RANGE:
				case CFG_NIC_SERIAL_NUMBER:
				case CFG_NIC_IDENTITY:
				case CFG_NIC_MFI_SUP_RANGE:
				case CFG_NIC_CFI_SUP_RANGE:
				case CFG_NIC_TEMP_TYPE:
				case CFG_NIC_PROFILE:
				case CFG_FW_IDENTITY:
				case CFG_FW_SUP_RANGE:
				case CFG_MFI_ACT_RANGES_STA:
				case CFG_CFI_ACT_RANGES_STA:
				case CFG_PORT_STAT:
				case CFG_CUR_SSID:
				case CFG_CUR_BSSID:
				case CFG_COMMS_QUALITY:
				case CFG_CUR_TX_RATE:
				case CFG_CUR_BEACON_INTERVAL:
				case CFG_CUR_SCALE_THRH:
				case CFG_PROTOCOL_RSP_TIME:
				case CFG_CUR_SHORT_RETRY_LIMIT:
				case CFG_CUR_LONG_RETRY_LIMIT:
				case CFG_MAX_TX_LIFETIME:
				case CFG_MAX_RX_LIFETIME:
				case CFG_CF_POLLABLE:
				case CFG_AUTHENTICATION_ALGORITHMS:
				case CFG_PRIVACY_OPT_IMPLEMENTED:
				//case CFG_CURRENT_REMOTE_RATES:
				//case CFG_CURRENT_USED_RATES:
				//case CFG_CURRENT_SYSTEM_SCALE:
				//case CFG_CURRENT_TX_RATE1:
				//case CFG_CURRENT_TX_RATE2:
				//case CFG_CURRENT_TX_RATE3:
				//case CFG_CURRENT_TX_RATE4:
				//case CFG_CURRENT_TX_RATE5:
				//case CFG_CURRENT_TX_RATE6:
				case CFG_NIC_MAC_ADDR:
				case CFG_PCF_INFO:
				//case CFG_CURRENT_COUNTRY_INFO:
				case CFG_PHY_TYPE:
				case CFG_CUR_CHANNEL:
				//case CFG_CURRENT_POWER_STATE:
				//case CFG_CCAMODE:
				case CFG_SUPPORTED_DATA_RATES:
					break;
				case CFG_AP_MODE:
//;?				lp->DownloadFirmware = ( pLtv->u.u16[0] ) + 1;
					DBG_ERROR( DbgInfo, "set CFG_AP_MODE no longer supported\n" );
					break;
				case CFG_ENCRYPT_STRING:
					/* TODO: ENDIAN TRANSLATION HERE??? */
					memset( lp->szEncryption, 0, sizeof( lp->szEncryption ));
					memcpy( (void *)lp->szEncryption,  (void *)&pLtv->u.u8[0],
							( pLtv->len * sizeof( hcf_16 )) );
					wl_wep_decode( CRYPT_CODE, &sEncryption,
								    lp->szEncryption );

					/* the Linux driver likes to use 1-4 for the key IDs, and then
					convert to 0-3 when sending to the card.  The Windows code
					base used 0-3 in the API DLL, which was ported to Linux.  For
					the sake of the user experience, we decided to keep 0-3 as the
					numbers used in the DLL; and will perform the +1 conversion here.
					We could have converted  the entire Linux driver, but this is
					less obtrusive.  This may be a "todo" to convert the whole driver */
					lp->TransmitKeyID    = sEncryption.wTxKeyID + 1;
					lp->EnableEncryption = sEncryption.wEnabled;

					memcpy( &lp->DefaultKeys, &sEncryption.EncStr,
							sizeof( CFG_DEFAULT_KEYS_STRCT ));
					break;
				/*case CFG_COUNTRY_STRING:
					memset( lp->countryString, 0, sizeof( lp->countryString ));
					memcpy( (void *)lp->countryString, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
					break;
				*/

				case CFG_DRIVER_ENABLE:
					lp->driverEnable    = pLtv->u.u16[0];
					pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_WOLAS_ENABLE:
					lp->wolasEnable = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_SET_WPA_AUTH_KEY_MGMT_SUITE:
					lp->AuthKeyMgmtSuite = pLtv->u.u16[0];
					pLtv->u.u16[0]  = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_DISASSOCIATE_ADDR:
					pLtv->u.u16[ETH_ALEN / 2] = CNV_INT_TO_LITTLE( pLtv->u.u16[ETH_ALEN / 2] );
					break;
				case CFG_ADD_TKIP_DEFAULT_KEY:
				case CFG_REMOVE_TKIP_DEFAULT_KEY:
					/* Endian convert the Tx Key Information */
					pLtv->u.u16[0] = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
					break;
				case CFG_ADD_TKIP_MAPPED_KEY:
					break;
				case CFG_REMOVE_TKIP_MAPPED_KEY:
					break;
				/* some RIDs just can't be put */
				case CFG_MB_INFO:
				case CFG_IFB:
				default:
					break;
				}

				/* This code will prevent Static Configuration Entities from
				   being sent to the card, as they require a call to
				   UIL_ACT_APPLY to take effect. Dynamic Entities will be sent
				   immediately */
				switch( pLtv->typ ) {
				case CFG_CNF_PORT_TYPE:
				case CFG_CNF_OWN_MAC_ADDR:
				case CFG_CNF_OWN_CHANNEL:
				case CFG_CNF_OWN_SSID:
				case CFG_CNF_OWN_ATIM_WINDOW:
				case CFG_CNF_SYSTEM_SCALE:
				case CFG_CNF_MAX_DATA_LEN:
				case CFG_CNF_PM_ENABLED:
				case CFG_CNF_MCAST_RX:
				case CFG_CNF_MAX_SLEEP_DURATION:
				case CFG_CNF_HOLDOVER_DURATION:
				case CFG_CNF_OWN_NAME:
				case CFG_CNF_LOAD_BALANCING:
				case CFG_CNF_MEDIUM_DISTRIBUTION:
#ifdef WARP 
				case CFG_CNF_TX_POW_LVL:
				case CFG_CNF_CONNECTION_CNTL:
				//case CFG_PROBE_DATA_RATE:
#endif // HERMES25
#if 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint
				case CFG_CNF_OWN_DTIM_PERIOD:
#ifdef WARP 
				case CFG_CNF_OWN_BEACON_INTERVAL:                    // Own Beacon Interval
#endif // WARP
#ifdef USE_WDS
				case CFG_CNF_WDS_ADDR1:
				case CFG_CNF_WDS_ADDR2:
				case CFG_CNF_WDS_ADDR3:
				case CFG_CNF_WDS_ADDR4:
				case CFG_CNF_WDS_ADDR5:
				case CFG_CNF_WDS_ADDR6:
#endif
				case CFG_CNF_MCAST_PM_BUF:
				case CFG_CNF_REJECT_ANY:
#endif

				case CFG_CNF_ENCRYPTION:
				case CFG_CNF_AUTHENTICATION:
#if 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint

				case CFG_CNF_EXCL_UNENCRYPTED:
				case CFG_CNF_MCAST_RATE:
				case CFG_CNF_INTRA_BSS_RELAY:
#endif

				case CFG_CNF_MICRO_WAVE:
				//case CFG_CNF_LOAD_BALANCING:
				//case CFG_CNF_MEDIUM_DISTRIBUTION:
				//case CFG_CNF_RX_ALL_GROUP_ADDRESS:
				//case CFG_CNF_COUNTRY_INFO:
				//case CFG_COUNTRY_STRING:
				case CFG_AP_MODE:
				case CFG_ENCRYPT_STRING:
				//case CFG_DRIVER_ENABLE:
				case CFG_WOLAS_ENABLE:
				case CFG_MB_INFO:
				case CFG_IFB:
					break;
				/* Deal with this dynamic MSF RID, as it's required for WPA */
				case CFG_DRIVER_ENABLE:
					if( lp->driverEnable ) {
						//hcf_cntl_port( &( lp->hcfCtx ),
						//               HCF_PORT_ENABLE | HCF_PORT_0 );
						// //hcf_cntl( &( lp->hcfCtx ),
						// //         HCF_PORT_ENABLE | HCF_PORT_0 );
						//hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_ENABLE );
						// //hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_CONNECT );

						hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_ENABLE | HCF_PORT_0 );
						hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_CONNECT );
					} else {
						//hcf_cntl_port( &( lp->hcfCtx ),
						//               HCF_PORT_DISABLE | HCF_PORT_0 );
						// //hcf_cntl( &( lp->hcfCtx ),
						// //         HCF_PORT_DISABLE | HCF_PORT_0 );
						//hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_DISABLE );
						// //hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_DISCONNECT );

						hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_DISABLE | HCF_PORT_0 );
						hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_DISCONNECT );
					}
					break;
				default:
					hcf_action(&(lp->hcfCtx), HCF_ACT_INT_OFF);
					urq->result = hcf_put_info(&(lp->hcfCtx), (LTVP) pLtv);
					hcf_action(&(lp->hcfCtx), HCF_ACT_INT_ON);
					break;
				}

				if( ltvAllocated ) {
					kfree( pLtv );
				}
			} else {
				urq->result = UIL_FAILURE;
			}
		} else {
			DBG_ERROR( DbgInfo, "EPERM\n" );
			urq->result = UIL_FAILURE;
			result = -EPERM;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_put_info
/*============================================================================*/

/*******************************************************************************
 *	wvlan_uil_get_info()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sends a specific RID directly to the driver to retrieve configuration
 *      info.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int wvlan_uil_get_info( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	int i;
	/*------------------------------------------------------------------------*/

	DBG_FUNC( "wvlan_uil_get_info" );
	DBG_ENTER( DbgInfo );

	if( urq->hcfCtx == &( lp->hcfCtx )) {
		if(( urq->data != NULL ) && ( urq->len != 0 )) {
			ltv_t      *pLtv;
			bool_t      ltvAllocated = FALSE;

			/* Make sure that we have at least a command and length */
			if( urq->len < ( sizeof( hcf_16 ) * 2 )) {
				urq->len = sizeof( lp->ltvRecord );
				DBG_ERROR( DbgInfo, "No Length/Type in LTV!!!\n" );
				DBG_ERROR( DbgInfo, "UIL_ERR_LEN\n" );
				urq->result = UIL_ERR_LEN;
				DBG_LEAVE( DbgInfo );
				return result;
			}

			/* Verify the user's LTV record header. */
			result = verify_area( VERIFY_READ, urq->data, sizeof( hcf_16 ) * 2 );
			if( result != 0 ) {
				DBG_ERROR( DbgInfo, "verify_area(), VERIFY_READ FAILED\n" );
				urq->result = UIL_FAILURE;
				DBG_LEAVE( DbgInfo );
				return result;
			}

			/* Get only the command and length information. */
			result = copy_from_user( &( lp->ltvRecord ), urq->data, sizeof( hcf_16 ) * 2 );

			/* Make sure the incoming LTV record length is within the bounds of
			   the IOCTL length. */
			if((( lp->ltvRecord.len + 1 ) * sizeof( hcf_16 )) > urq->len ) {
				DBG_ERROR( DbgInfo, "Incoming LTV too big\n" );
				urq->len = sizeof( lp->ltvRecord );
				urq->result = UIL_ERR_LEN;
				DBG_LEAVE( DbgInfo );
				return result;
			}

			/* Determine if hcf_get_info() is needed or not */
			switch ( lp->ltvRecord.typ ) {
			case CFG_NIC_IDENTITY:
				memcpy( &lp->ltvRecord.u.u8[0], &lp->NICIdentity, sizeof( lp->NICIdentity ));
				break;
			case CFG_PRI_IDENTITY:
				memcpy( &lp->ltvRecord.u.u8[0], &lp->PrimaryIdentity, sizeof( lp->PrimaryIdentity ));
				break;
			case CFG_AP_MODE:
				DBG_ERROR( DbgInfo, "set CFG_AP_MODE no longer supported, so is get useful ????\n" );
				lp->ltvRecord.u.u16[0] = 
					CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_AP;
				break;
			//case CFG_DRV_INFO:
			case CFG_ENCRYPT_STRING:
			case CFG_COUNTRY_STRING:
			case CFG_DRIVER_ENABLE:
			case CFG_WOLAS_ENABLE:
				// TODO: determine if we're going to support these
				urq->result = UIL_FAILURE;
				break;
			case CFG_DRV_INFO:
				DBG_TRACE( DbgInfo, "Intercept CFG_DRV_INFO\n" );
				result = cfg_driver_info( urq, lp );
				break;
			case CFG_DRV_IDENTITY:
				DBG_TRACE( DbgInfo, "Intercept CFG_DRV_IDENTITY\n" );
				result = cfg_driver_identity( urq, lp );
				break;
			case CFG_IFB:
				/* IFB can be a security hole */
				if( !capable( CAP_NET_ADMIN )) {
					result = -EPERM;
					break;
				}

				/* Else fall through to the default */

			case CFG_FW_IDENTITY:   // For Hermes-1, this is cached
			default:

				/* Verify the user buffer */
				result = verify_area( VERIFY_WRITE, urq->data, urq->len );
				if( result != 0 ) {
					DBG_ERROR( DbgInfo, "verify_area(), VERIFY_WRITE FAILED\n" );
					urq->result = UIL_FAILURE;
					break;
				}

				/* If the requested length is greater than the size of our local
				   LTV record, try to allocate it from the kernel stack.
				   Otherwise, we just use our local LTV record. */
				if( urq->len > sizeof( lp->ltvRecord )) {
					if(( pLtv = (ltv_t *)kmalloc( urq->len, GFP_KERNEL )) != NULL ) {
						ltvAllocated = TRUE;

						/* Copy the command/length information into the new buffer. */
						memcpy( pLtv, &( lp->ltvRecord ), sizeof( hcf_16 ) * 2 );
					} else {
						urq->len = sizeof( lp->ltvRecord );
						urq->result = UIL_ERR_LEN;
						DBG_ERROR( DbgInfo, "kmalloc FAILED\n" );
						DBG_ERROR( DbgInfo, "UIL_ERR_LEN\n" );
						result = -ENOMEM;
						break;
					}
				} else {
					pLtv = &( lp->ltvRecord );
				}

				hcf_action( &( lp->hcfCtx ), HCF_ACT_INT_OFF );
				urq->result = hcf_get_info( &( lp->hcfCtx ), (LTVP) pLtv );
				hcf_action( &( lp->hcfCtx ), HCF_ACT_INT_ON );

				// Copy the LTV into the user's buffer.
				//copy_to_user( urq->data, pLtv, urq->len );

				//if( ltvAllocated )
				//{
				//    kfree( pLtv );
				//}

				//urq->result = UIL_SUCCESS;
				break;
			}

			/* Handle endian conversion of special fields */
			switch( lp->ltvRecord.typ ) {
			/* simple int gets just need the first hcf_16 byte flipped */
			case CFG_CNF_PORT_TYPE:
			case CFG_CNF_OWN_CHANNEL:
			case CFG_CNF_OWN_ATIM_WINDOW:
			case CFG_CNF_SYSTEM_SCALE:
			case CFG_CNF_MAX_DATA_LEN:
			case CFG_CNF_PM_ENABLED:
			case CFG_CNF_MCAST_RX:
			case CFG_CNF_MAX_SLEEP_DURATION:
			case CFG_CNF_HOLDOVER_DURATION:
			case CFG_CNF_OWN_DTIM_PERIOD:
			case CFG_CNF_MCAST_PM_BUF:
			case CFG_CNF_REJECT_ANY:
			case CFG_CNF_ENCRYPTION:
			case CFG_CNF_AUTHENTICATION:
			case CFG_CNF_EXCL_UNENCRYPTED:
			case CFG_CNF_INTRA_BSS_RELAY:
			case CFG_CNF_MICRO_WAVE:
			case CFG_CNF_LOAD_BALANCING:
			case CFG_CNF_MEDIUM_DISTRIBUTION:
#ifdef WARP
			case CFG_CNF_TX_POW_LVL:
			case CFG_CNF_CONNECTION_CNTL:
			case CFG_CNF_OWN_BEACON_INTERVAL:                          // Own Beacon Interval
			case CFG_COEXISTENSE_BEHAVIOUR:                            // Coexistence Behavior
			//case CFG_CNF_RX_ALL_GROUP_ADDRESS:
#endif // HERMES25
			case CFG_CREATE_IBSS:
			case CFG_RTS_THRH:
			case CFG_PROMISCUOUS_MODE:
			//case CFG_WAKE_ON_LAN:
			case CFG_RTS_THRH0:
			case CFG_RTS_THRH1:
			case CFG_RTS_THRH2:
			case CFG_RTS_THRH3:
			case CFG_RTS_THRH4:
			case CFG_RTS_THRH5:
			case CFG_RTS_THRH6:
			case CFG_TX_RATE_CNTL0:
			case CFG_TX_RATE_CNTL1:
			case CFG_TX_RATE_CNTL2:
			case CFG_TX_RATE_CNTL3:
			case CFG_TX_RATE_CNTL4:
			case CFG_TX_RATE_CNTL5:
			case CFG_TX_RATE_CNTL6:
			case CFG_TX_KEY_ID:
			case CFG_TICK_TIME:
			case CFG_MAX_LOAD_TIME:
			case CFG_NIC_TEMP_TYPE:
			case CFG_PORT_STAT:
			case CFG_CUR_TX_RATE:
			case CFG_CUR_BEACON_INTERVAL:
			case CFG_PROTOCOL_RSP_TIME:
			case CFG_CUR_SHORT_RETRY_LIMIT:
			case CFG_CUR_LONG_RETRY_LIMIT:
			case CFG_MAX_TX_LIFETIME:
			case CFG_MAX_RX_LIFETIME:
			case CFG_CF_POLLABLE:
			case CFG_PRIVACY_OPT_IMPLEMENTED:
			//case CFG_CURRENT_REMOTE_RATES:
			//case CFG_CURRENT_USED_RATES:
			//case CFG_CURRENT_SYSTEM_SCALE:
			//case CFG_CURRENT_TX_RATE1:
			//case CFG_CURRENT_TX_RATE2:
			//case CFG_CURRENT_TX_RATE3:
			//case CFG_CURRENT_TX_RATE4:
			//case CFG_CURRENT_TX_RATE5:
			//case CFG_CURRENT_TX_RATE6:
			case CFG_PHY_TYPE:
			case CFG_CUR_CHANNEL:
			//case CFG_CURRENT_POWER_STATE:
			//case CFG_CCAMODE:
			//    lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
			//    break;
			/* name string gets just need the first hcf_16 byte flipped (length of string) */
			case CFG_CNF_OWN_SSID:
			case CFG_CNF_OWN_NAME:
			//case CNF_DESIRED_SSID:
			case CFG_DESIRED_SSID:
			case CFG_SCAN_SSID:
			case CFG_CUR_SSID:
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				break;
			/* non-length counted strings need no byte flipping */
			case CFG_CNF_OWN_MAC_ADDR:
			/* this case is no longer valid: CFG_CNF_WDS_ADDR */
			case CFG_CNF_WDS_ADDR1:
			case CFG_CNF_WDS_ADDR2:
			case CFG_CNF_WDS_ADDR3:
			case CFG_CNF_WDS_ADDR4:
			case CFG_CNF_WDS_ADDR5:
			case CFG_CNF_WDS_ADDR6:
			case CFG_GROUP_ADDR:
			case CFG_NIC_SERIAL_NUMBER:
			case CFG_CUR_BSSID:
			case CFG_NIC_MAC_ADDR:
			case CFG_SUPPORTED_DATA_RATES:  /* need to ensure we can treat this as a string */
				break;
			//case CFG_CNF_COUNTRY_INFO:      /* special case, see page 75  of 022486, Rev C. */
			//case CFG_CURRENT_COUNTRY_INFO:  /* special case, see page 101 of 022486, Rev C. */
			/*
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				lp->ltvRecord.u.u16[3] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[3] );

				for( i = 4; i < lp->ltvRecord.len; i++ ) {
					lp->ltvRecord.u.u16[i] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[i] );
				}
				break;
			*/

			case CFG_DEFAULT_KEYS:
				{
					CFG_DEFAULT_KEYS_STRCT *pKeys = (CFG_DEFAULT_KEYS_STRCT *)&lp->ltvRecord.u.u8[0];

					pKeys[0].len = CNV_INT_TO_LITTLE( pKeys[0].len );
					pKeys[1].len = CNV_INT_TO_LITTLE( pKeys[1].len );
					pKeys[2].len = CNV_INT_TO_LITTLE( pKeys[2].len );
					pKeys[3].len = CNV_INT_TO_LITTLE( pKeys[3].len );
				}
				break;
			case CFG_CNF_MCAST_RATE:
			case CFG_TX_RATE_CNTL:
			case CFG_SUPPORTED_RATE_SET_CNTL:    // Supported Rate Set Control
			case CFG_BASIC_RATE_SET_CNTL:    // Basic Rate Set Control
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				lp->ltvRecord.u.u16[1] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[1] );
				break;
			case CFG_DL_BUF:
			case CFG_NIC_IDENTITY:
			case CFG_COMMS_QUALITY:
			case CFG_PCF_INFO:
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				lp->ltvRecord.u.u16[1] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[1] );
				lp->ltvRecord.u.u16[2] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[2] );
				break;
			case CFG_FW_IDENTITY:
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				lp->ltvRecord.u.u16[1] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[1] );
				lp->ltvRecord.u.u16[2] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[2] );
				lp->ltvRecord.u.u16[3] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[3] );
				break;
			//case CFG_HSI_SUP_RANGE:
			case CFG_NIC_MFI_SUP_RANGE:
			case CFG_NIC_CFI_SUP_RANGE:
			case CFG_NIC_PROFILE:
			case CFG_FW_SUP_RANGE:
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[0] );
				lp->ltvRecord.u.u16[1] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[1] );
				lp->ltvRecord.u.u16[2] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[2] );
				lp->ltvRecord.u.u16[3] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[3] );
				lp->ltvRecord.u.u16[4] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[4] );
				break;
			case CFG_MFI_ACT_RANGES_STA:
			case CFG_CFI_ACT_RANGES_STA:
			case CFG_CUR_SCALE_THRH:
			case CFG_AUTHENTICATION_ALGORITHMS:
				for( i = 0; i < ( lp->ltvRecord.len - 1 ); i++ ) {
					lp->ltvRecord.u.u16[i] = CNV_INT_TO_LITTLE( lp->ltvRecord.u.u16[i] );
				}
				break;
			/* done at init time, and endian handled then */
			case CFG_PRI_IDENTITY:
				break;
			case CFG_MB_INFO:
				//wvlanEndianTranslateMailbox( pLtv );
				break;
			/* MSF and HCF RIDS */
			case CFG_IFB:
			case CFG_DRV_INFO:
			case CFG_AP_MODE:
			case CFG_ENCRYPT_STRING:
			case CFG_COUNTRY_STRING:
			case CFG_DRIVER_ENABLE:
			case CFG_WOLAS_ENABLE:
			default:
				break;
			}

			// Copy the LTV into the user's buffer.
			copy_to_user( urq->data, &( lp->ltvRecord ), urq->len );

			if( ltvAllocated ) {
				kfree( &( lp->ltvRecord ));
			}

			urq->result = UIL_SUCCESS;
		} else {
			urq->result = UIL_FAILURE;
		}
	} else {
		DBG_ERROR( DbgInfo, "UIL_ERR_WRONG_IFB\n" );
		urq->result = UIL_ERR_WRONG_IFB;
	}

	DBG_LEAVE( DbgInfo );
	return result;
} // wvlan_uil_get_info
/*============================================================================*/





/*******************************************************************************
 *	cfg_driver_info()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Retrieves driver information.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int cfg_driver_info( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "cfg_driver_info" );
	DBG_ENTER( DbgInfo );


	/* Make sure that user buffer can handle the driver information buffer */
	if( urq->len < sizeof( lp->driverInfo )) {
		urq->len = sizeof( lp->driverInfo );
		urq->result = UIL_ERR_LEN;
		DBG_LEAVE( DbgInfo );
		return result;
	}

	/* Verify the user buffer. */
	result = verify_area( VERIFY_WRITE, urq->data, sizeof( lp->driverInfo ));
	if( result != 0 ) {
		urq->result = UIL_FAILURE;
		DBG_LEAVE( DbgInfo );
		return result;
	}

	lp->driverInfo.card_stat = lp->hcfCtx.IFB_CardStat;

	// Copy the driver information into the user's buffer.
	urq->result = UIL_SUCCESS;
	copy_to_user( urq->data, &( lp->driverInfo ), sizeof( lp->driverInfo ));

	DBG_LEAVE( DbgInfo );
	return result;
} // cfg_driver_info
/*============================================================================*/




/*******************************************************************************
 *	cfg_driver_identity()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Retrieves ID information from the card.
 *
 *  PARAMETERS:
 *
 *      urq - a pointer to the UIL request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      UIL_SUCCESS
 *      UIL_ERR_xxx value otherwise
 *
 ******************************************************************************/
int cfg_driver_identity( struct uilreq *urq, struct wl_private *lp )
{
	int result = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_driver_identity" );
	DBG_ENTER( DbgInfo );


	/* Make sure that user buffer can handle the driver identity structure. */
	if( urq->len < sizeof( lp->driverIdentity )) {
		urq->len = sizeof( lp->driverIdentity );
		urq->result = UIL_ERR_LEN;
		DBG_LEAVE( DbgInfo );
		return result;
	}

	/* Verify the user buffer. */
	result = verify_area( VERIFY_WRITE, urq->data, sizeof( lp->driverIdentity ));
	if( result != 0 ) {
		urq->result = UIL_FAILURE;
		DBG_LEAVE( DbgInfo );
		return result;
	}

	/* Copy the driver identity into the user's buffer. */
	urq->result = UIL_SUCCESS;
	copy_to_user( urq->data, &( lp->driverIdentity ), sizeof( lp->driverIdentity ));

	DBG_LEAVE( DbgInfo );
	return result;
} // cfg_driver_identity
/*============================================================================*/


#endif  /* USE_UIL */


/* If WIRELESS_EXT is not defined, then the functions that follow will not be
   included in the build. */
/* NOTE: Are these still even needed? */
#ifdef WIRELESS_EXT


/*******************************************************************************
 *	wvlan_set_netname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Set the ESSID of the card.
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_set_netname( struct iwreq *wrq, struct wl_private *lp )
{
	int ret = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_set_netname" );
	DBG_ENTER( DbgInfo );


	if( !capable( CAP_NET_ADMIN )) {
		ret = -EPERM;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Validate the new value */
	if( wrq->u.data.length > HCF_MAX_NAME_LEN ) {
		ret = -EINVAL;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	ret = verify_area( VERIFY_READ, wrq->u.data.pointer, wrq->u.data.length );
	if( ret != 0 ) {
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	memset( lp->NetworkName, 0, sizeof( lp->NetworkName ));
	copy_from_user( lp->NetworkName, wrq->u.data.pointer, wrq->u.data.length );

	/* Commit the adapter parameters */
	wl_apply(lp);

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_set_netname
/*============================================================================*/




/*******************************************************************************
 *	wvlan_get_netname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Get the ESSID of the card.
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_get_netname( struct iwreq *wrq, struct wl_private *lp )
{
	int         ret = 0;
	int         status = -1;
	wvName_t   *pName;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_get_netname" );
	DBG_ENTER( DbgInfo );

	ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, HCF_MAX_NAME_LEN );

	if( ret != 0 ) {
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Get the current network name */
	lp->ltvRecord.len = 1 + ( sizeof( *pName ) / sizeof( hcf_16 ));
	lp->ltvRecord.typ = CFG_CUR_SSID;

	status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));

	if( status == HCF_SUCCESS ) {
		pName = (wvName_t *)&( lp->ltvRecord.u.u32 );

		/* Copy the information into the user buffer */
		wrq->u.data.length = pName->length + 1;

		copy_to_user( wrq->u.data.pointer, pName->name, pName->length + 1 );
	} else {
		ret = -EIO;
	}

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_get_netname
/*============================================================================*/




/*******************************************************************************
 *	wvlan_set_station_nickname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Set the card's station nickname.
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_set_station_nickname( struct iwreq *wrq, struct wl_private *lp )
{
	int ret = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_set_station_nickname" );
	DBG_ENTER( DbgInfo );

	if( !capable( CAP_NET_ADMIN )) {
		ret = -EPERM;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Validate the new value */
	if( wrq->u.data.length > HCF_MAX_NAME_LEN ) {
		ret = -EINVAL;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	ret = verify_area( VERIFY_READ, wrq->u.data.pointer, wrq->u.data.length );

	if( ret != 0 ) {
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	memset( lp->StationName, 0, sizeof( lp->StationName ));

	copy_from_user( lp->StationName, wrq->u.data.pointer, wrq->u.data.length );

	/* Commit the adapter parameters */
	wl_apply( lp );

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_set_station_nickname
/*============================================================================*/




/*******************************************************************************
 *	wvlan_get_station_nickname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Get the card's station nickname.
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_get_station_nickname( struct iwreq *wrq, struct wl_private *lp )
{
	int         ret = 0;
	int         status = -1;
	wvName_t    *pName;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_get_station_nickname" );
	DBG_ENTER( DbgInfo );

	ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, HCF_MAX_NAME_LEN );

	if( ret != 0 ) {
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Get the current station name */
	lp->ltvRecord.len = 1 + ( sizeof( *pName ) / sizeof( hcf_16 ));
	lp->ltvRecord.typ = CFG_CNF_OWN_NAME;

	status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));

	if( status == HCF_SUCCESS ) {
		pName = (wvName_t *)&( lp->ltvRecord.u.u32 );

		/* Copy the information into the user buffer */
		wrq->u.data.length = pName->length + 1;

		copy_to_user( wrq->u.data.pointer, pName->name, pName->length + 1 );
	} else {
		ret = -EIO;
	}

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_get_station_nickname
/*============================================================================*/




/*******************************************************************************
 *	wvlan_set_porttype()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Set the card's porttype
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_set_porttype( struct iwreq *wrq, struct wl_private *lp )
{
	int     ret = 0;
	hcf_16  portType;
	hcf_16  *pData;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_set_porttype" );
	DBG_ENTER( DbgInfo );

	if( !capable( CAP_NET_ADMIN )) {
		ret = -EPERM;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Validate the new value */
	pData = (hcf_16 *)&( wrq->u.data.pointer );
	portType = *pData;

	if( !(( portType == 1 ) || ( portType == 3 ))) {
		ret = -EINVAL;
		DBG_LEAVE( DbgInfo );
		return ret;
	}

	lp->PortType = portType;

	/* Commit the adapter parameters */
	wl_apply( lp );

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_set_porttype
/*============================================================================*/


/*******************************************************************************
 *	wvlan_get_porttype()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Get the card's porttype
 *
 *  PARAMETERS:
 *
 *      wrq - a pointer to the wireless request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_get_porttype( struct iwreq *wrq, struct wl_private *lp )
{
	int     ret = 0;
	int     status = -1;
	hcf_16  *pPortType;
	hcf_16  *pData;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_get_porttype" );
	DBG_ENTER( DbgInfo );

	/* Get the current port type */
	lp->ltvRecord.len = 1 + ( sizeof( *pPortType ) / sizeof( hcf_16 ));
	lp->ltvRecord.typ = CFG_CNF_PORT_TYPE;

	status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));

	if( status == HCF_SUCCESS ) {
		pPortType = (hcf_16 *)&( lp->ltvRecord.u.u32 );

		pData = (hcf_16 *)&( wrq->u.data.pointer );
		*pData = CNV_LITTLE_TO_INT( *pPortType );
	} else {
		ret = -EIO;
	}

	DBG_LEAVE( DbgInfo );
	return ret;
} // wvlan_get_porttype
/*============================================================================*/

#endif  // WIRELESS_EXT




#ifdef USE_RTS
/*******************************************************************************
 *	wvlan_rts()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      IOCTL handler for RTS commands
 *
 *  PARAMETERS:
 *
 *      rrq - a pointer to the rts request buffer
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_rts( struct rtsreq *rrq, __u32 io_base )
{
	int ioctl_ret = 0;
	/*------------------------------------------------------------------------*/


	DBG_FUNC( "wvlan_rts" );
	DBG_ENTER( DbgInfo );


	DBG_PRINT( "io_base: 0x%08x\n", io_base );

	switch( rrq->typ ) {
	  case WL_IOCTL_RTS_READ:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_READ\n");
		rrq->data[0] = IN_PORT_WORD( io_base + rrq->reg );
		DBG_TRACE( DbgInfo, "  reg 0x%04x ==> 0x%04x\n", rrq->reg, CNV_LITTLE_TO_SHORT( rrq->data[0] ) );
		break;
	  case WL_IOCTL_RTS_WRITE:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_WRITE\n");
		OUT_PORT_WORD( io_base + rrq->reg, rrq->data[0] );
		DBG_TRACE( DbgInfo, "  reg 0x%04x <== 0x%04x\n", rrq->reg, CNV_LITTLE_TO_SHORT( rrq->data[0] ) );
		break;
	  case WL_IOCTL_RTS_BATCH_READ:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_BATCH_READ\n");
		IN_PORT_STRING_16( io_base + rrq->reg, rrq->data, rrq->len );
		DBG_TRACE( DbgInfo, "  reg 0x%04x ==> %d bytes\n", rrq->reg, rrq->len * sizeof (__u16 ) );
		break;
	  case WL_IOCTL_RTS_BATCH_WRITE:
		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_BATCH_WRITE\n");
		OUT_PORT_STRING_16( io_base + rrq->reg, rrq->data, rrq->len );
		DBG_TRACE( DbgInfo, "  reg 0x%04x <== %d bytes\n", rrq->reg, rrq->len * sizeof (__u16) );
		break;
	default:

		DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- UNSUPPORTED RTS CODE: 0x%X", rrq->typ );
		ioctl_ret = -EOPNOTSUPP;
		break;
	}

	DBG_LEAVE( DbgInfo );
	return ioctl_ret;
} // wvlan_rts
/*============================================================================*/

#endif  /* USE_RTS */
