/*******************************************************************************
 * 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: vjs $
 * $Date: 2004/02/16 17:58:12 $
 * $Revision: 1.40 $
 * $Source: /cvsroot/wifi/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>




/*******************************************************************************
 * 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_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_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_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 ), HCF_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_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_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_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_PIFRscInd != 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_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;


#if (HCF_TYPE) & HCF_TYPE_AP

                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 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 (HCF_TYPE) & HCF_TYPE_AP

                //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:

                    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   = pLtv->u.u16[0];
                    pLtv->u.u16[0]      = CNV_INT_TO_LITTLE( pLtv->u.u16[0] );
                    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 #if (HCF_TYPE) & HCF_TYPE_AP
                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:

                    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_MFI_SUP_RANGE:
                case CFG_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_CURRENT_SSID:
                case CFG_CURRENT_BSSID:
                case CFG_COMMS_QUALITY:
                case CFG_CURRENT_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_CURRENT_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;
                    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:

#if (HCF_TYPE) & HCF_TYPE_AP
                case CFG_CNF_OWN_DTIM_PERIOD:
#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 (HCF_TYPE) & HCF_TYPE_AP

                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_CNTL_ENABLE | HCF_PORT_0 );
                    }
                    else
                    {
                        //hcf_cntl_port( &( lp->hcfCtx ),
                        //               HCF_PORT_DISABLE | HCF_PORT_0 );
                        hcf_cntl( &( lp->hcfCtx ), HCF_CNTL_DISABLE | HCF_PORT_0 );
                    }
                    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_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:
                if( lp->DownloadFirmware == WVLAN_DRV_MODE_AP )
                {
                    lp->ltvRecord.u.u16[0] = TRUE;
                }
                else
                {
                    lp->ltvRecord.u.u16[0] = FALSE;
                }
                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_MCAST_RATE:
            case CFG_CNF_INTRA_BSS_RELAY:
            case CFG_CNF_MICRO_WAVE:
            case CFG_CNF_LOAD_BALANCING:
            case CFG_CNF_MEDIUM_DISTRIBUTION:
            //case CFG_CNF_RX_ALL_GROUP_ADDRESS:
            case CFG_CREATE_IBSS:
            case CFG_RTS_THRH:
            case CFG_TX_RATE_CNTL:
            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_CURRENT_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_CURRENT_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_SCAN_SSID:
            case CFG_CURRENT_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_CURRENT_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_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_MFI_SUP_RANGE:
            case CFG_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;
}
/*============================================================================*/





/*******************************************************************************
 *	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_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;
}
/*============================================================================*/


#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_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_CURRENT_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_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_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_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_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;
}
/*============================================================================*/

#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");
        ioctl_ret = wvlan_rts_read( rrq->reg, &rrq->data[0], io_base );

        break;


    case WL_IOCTL_RTS_WRITE:

        DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_WRITE\n");
        ioctl_ret = wvlan_rts_write( rrq->reg, rrq->data[0], io_base );

        break;


    case WL_IOCTL_RTS_BATCH_READ:

        DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_BATCH_READ\n");
        ioctl_ret = wvlan_rts_batch_read( rrq, io_base );

        break;


    case WL_IOCTL_RTS_BATCH_WRITE:

        DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_RTS -- WL_IOCTL_RTS_BATCH_WRITE\n");
        ioctl_ret = wvlan_rts_batch_write( rrq, io_base );

        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_read()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      RTS function to read a Hermes register
 *
 *  PARAMETERS:
 *
 *      reg - the register to read
 *      val - a pointer to a unsigned 16 in which the value will be stored
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_rts_read( __u16 reg, __u16 *val, __u32 io_base )
{
    __u16   value;
    hcf_io  port;
    /*------------------------------------------------------------------------*/


    DBG_PRINT( "reg     :     0x%04x\n", reg );
    DBG_PRINT( "val     :     0x%04x\n", *val );
    DBG_PRINT( "io_base : 0x%08x\n", io_base );


    port = io_base + (hcf_io)reg;
    value = IN_PORT_WORD( port );
    
    mdelay( 5 );   

    value = CNV_INT_TO_LITTLE( value );

    *val = value;

    DBG_PRINT( "reg     :     0x%04x\n", reg );
    DBG_PRINT( "val     :     0x%04x\n", *val );
    DBG_PRINT( "io_base : 0x%08x\n", io_base );

    return 0;
}
/*============================================================================*/




/*******************************************************************************
 *	wvlan_rts_write()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      RTS function to write a Hermes register
 *
 *  PARAMETERS:
 *
 *      reg - the register to write
 *      val - the unsigned 16 value to write
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_rts_write( __u16 reg, __u16 val, __u32 io_base )
{
    hcf_io  port;
    /*------------------------------------------------------------------------*/


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

    val  = CNV_INT_TO_LITTLE( val );
    port = io_base + (hcf_io)reg;

    OUT_PORT_WORD( port, val );

    return 0;
}
/*============================================================================*/




/*******************************************************************************
 *	wvlan_rts_batch_read()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      RTS function to read a set of Hermes registers
 *
 *  PARAMETERS:
 *
 *      len - the size of area to read
 *      reg - the register from which to start reading
 *      buf - a pointer to an array of unsigned 16 in which the values will be
 *            stored
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_rts_batch_read( struct rtsreq *rrq, __u32 io_base )
{
    int   result = 0;
    __u16 count;
    /*------------------------------------------------------------------------*/


    if( !rrq->data || rrq->reg >= 0x40 )
    {
        return -EINVAL;
    }

    for( count = 0; count < rrq->len; count++ )
    {
        result = wvlan_rts_read( rrq->reg, &rrq->data[count], io_base );

        if( result != 0 )
        {
            break;
        }
    }

    return result;
}
/*============================================================================*/




/*******************************************************************************
 *	wvlan_rts_batch_write()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      RTS function to write a set of Hermes registers
 *
 *  PARAMETERS:
 *
 *      len - the size of area to write
 *      reg - the register from which to start writing
 *      buf - a pointer to an array of unsigned 16 values to write
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wvlan_rts_batch_write( struct rtsreq *rrq, __u32 io_base )
{
    int   result = 0;
    __u16 count;
    /*------------------------------------------------------------------------*/


    if( !rrq->data || rrq->reg >= 0x40 )
    {
        return -EINVAL;
    }

    for( count = 0; count < rrq->len; count++ )
    {
        result = wvlan_rts_write( rrq->reg, rrq->data[count], io_base );

        if( result != 0 )
        {
            break;
        }
    }

    return result;
}
/*============================================================================*/
#endif  /* USE_RTS */
