/*******************************************************************************
 * 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 contains processing and initialization specific to PCI/miniPCI
 *   devices.
 *
 *------------------------------------------------------------------------------
 *
 * 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/06 11:25:37 $
 * $Revision: 1.7 $
 * $Source: /usr/local/cvs/wl_lkm/wireless/wl_pci.c,v $
 *
 ******************************************************************************/



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

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/string.h>
//#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>

#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>

#include <hcf/debug.h>

#include <hcf.h>
#include <dhf.h>
#include <hcfdef.h>

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


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

/* define the PCI device Table Cardname and id tables */
enum hermes_pci_versions {
	CH_Agere_Systems_Mini_PCI_V1 = 0,
};

static struct pci_device_id wl_pci_tbl[] __devinitdata = {
	{ WL_LKM_PCI_VENDOR_ID, WL_LKM_PCI_DEVICE_ID_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Agere_Systems_Mini_PCI_V1 },
    { WL_LKM_PCI_VENDOR_ID, WL_LKM_PCI_DEVICE_ID_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Agere_Systems_Mini_PCI_V1 },
    { WL_LKM_PCI_VENDOR_ID, WL_LKM_PCI_DEVICE_ID_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Agere_Systems_Mini_PCI_V1 },
	{ }			/* Terminating entry */
};

MODULE_DEVICE_TABLE(pci, wl_pci_tbl);

/*******************************************************************************
 * function prototypes
 ******************************************************************************/
int __devinit wl_pci_probe( struct pci_dev *pdev,
                                const struct pci_device_id *ent );
void __devexit wl_pci_remove(struct pci_dev *pdev);
int wl_pci_setup( struct pci_dev *pdev );
void wl_pci_enable_cardbus_interrupts( struct pci_dev *pdev );

#ifdef ENABLE_DMA
int wl_pci_dma_alloc( struct pci_dev *pdev, struct wl_private *lp );
int wl_pci_dma_free( struct pci_dev *pdev, struct wl_private *lp );
int wl_pci_dma_alloc_tx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc );
int wl_pci_dma_free_tx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc );
int wl_pci_dma_alloc_rx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc );
int wl_pci_dma_free_rx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc );
int wl_pci_dma_alloc_desc_and_buf( struct pci_dev *pdev, struct wl_private *lp,
                                   DESC_STRCT **desc, int size );
int wl_pci_dma_free_desc_and_buf( struct pci_dev *pdev, struct wl_private *lp,
                                   DESC_STRCT **desc );
int wl_pci_dma_alloc_desc( struct pci_dev *pdev, struct wl_private *lp,
                           DESC_STRCT **desc );
int wl_pci_dma_free_desc( struct pci_dev *pdev, struct wl_private *lp,
                           DESC_STRCT **desc );
int wl_pci_dma_alloc_buf( struct pci_dev *pdev, struct wl_private *lp,
                          DESC_STRCT *desc, int size );
int wl_pci_dma_free_buf( struct pci_dev *pdev, struct wl_private *lp,
                          DESC_STRCT *desc );

void wl_pci_dma_hcf_reclaim_rx( struct wl_private *lp );
#endif  // ENABLE_DMA

/*******************************************************************************
 * PCI module function registration
 ******************************************************************************/
static struct pci_driver wl_driver =
{
	name:		MODULE_NAME,
    id_table:	wl_pci_tbl,
	probe:		wl_pci_probe,
	remove:		__devexit_p(wl_pci_remove),
    suspend:    NULL,
    resume:     NULL,
};

/*******************************************************************************
 *	wl_adapter_init_module()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Called by init_module() to perform PCI-specific driver initialization.
 *
 *  PARAMETERS:
 *
 *      N/A
 *
 *  RETURNS:
 *
 *      0
 *
 ******************************************************************************/
int wl_adapter_init_module( void )
{
    int result;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_adapter_init_module()" );
    DBG_ENTER( DbgInfo );
    DBG_TRACE( DbgInfo, "wl_adapter_init_module() -- PCI\n" );

    result = pci_register_driver( &wl_driver ); //;?replace with pci_module_init, Rubini pg 490
	//;? why not do something with the result
    
    DBG_LEAVE( DbgInfo );
    return 0;
} // wl_adapter_init_module
/*============================================================================*/

/*******************************************************************************
 *	wl_adapter_cleanup_module()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Called by cleanup_module() to perform PCI-specific driver cleanup.
 *
 *  PARAMETERS:
 *
 *      N/A
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_adapter_cleanup_module( void )
{
	//;?how comes wl_adapter_cleanup_module is located in a seemingly pci specific module
    DBG_FUNC( "wl_adapter_cleanup_module" );
    DBG_ENTER( DbgInfo );

	//;?DBG_TRACE below feels like nearly redundant in the light of DBG_ENTER above
    DBG_TRACE( DbgInfo, "wl_adapter_cleanup_module() -- PCI\n" );

    pci_unregister_driver( &wl_driver );

    DBG_LEAVE( DbgInfo );
    return;
} // wl_adapter_cleanup_module
/*============================================================================*/

/*******************************************************************************
 *	wl_adapter_insert()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Called by wl_pci_probe() to continue the process of device insertion. 
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      TRUE or FALSE
 *
 ******************************************************************************/
int wl_adapter_insert( struct net_device *dev )
{
    int result = FALSE;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_adapter_insert" );
    DBG_ENTER( DbgInfo );

    DBG_TRACE( DbgInfo, "wl_adapter_insert() -- PCI\n" );

    if( dev == NULL ) {
        DBG_ERROR( DbgInfo, "net_device pointer is NULL!!!\n" );
    } else if( dev->priv == NULL ) {
        DBG_ERROR( DbgInfo, "wl_private pointer is NULL!!!\n" );
    } else if( wl_insert( dev ) ) { /* Perform remaining device initialization */
		result = TRUE;
	} else {
        DBG_TRACE( DbgInfo, "wl_insert() FAILED\n" );
    }
    DBG_LEAVE( DbgInfo );
    return result;
} // wl_adapter_insert
/*============================================================================*/

/*******************************************************************************
 *	wl_adapter_open()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Open the device.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      an HCF status code
 *
 ******************************************************************************/
int wl_adapter_open( struct net_device *dev )
{
    int         result = 0;
    int         hcf_status = HCF_SUCCESS;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_adapter_open" );
    DBG_ENTER( DbgInfo );

    DBG_TRACE( DbgInfo, "wl_adapter_open() -- PCI\n" );
    
    hcf_status = wl_open( dev );

    if( hcf_status != HCF_SUCCESS ) {
        result = -ENODEV;
    }

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

/*******************************************************************************
 *	wl_adapter_close()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Close the device
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      0
 *
 ******************************************************************************/
int wl_adapter_close( struct net_device *dev )
{
    DBG_FUNC( "wl_adapter_close" );
    DBG_ENTER( DbgInfo );

    DBG_TRACE( DbgInfo, "wl_adapter_close() -- PCI\n" );
    DBG_TRACE( DbgInfo, "%s: Shutting down adapter.\n", dev->name );
    
    wl_close( dev );

    DBG_LEAVE( DbgInfo );
    return 0;
} // wl_adapter_close
/*============================================================================*/

/*******************************************************************************
 *	wl_adapter_is_open()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Check whether this device is open. Returns 
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      nonzero if device is open.
 *
 ******************************************************************************/
int wl_adapter_is_open( struct net_device *dev )
{
    /* This function is used in PCMCIA to check the status of the 'open' field
       in the dev_link_t structure associated with a network device. There
       doesn't seem to be an analog to this for PCI, and checking the status 
       contained in the net_device structure doesn't have the same effect. 
       For now, return TRUE, but find out if this is necessary for PCI. */

    return TRUE;
} // wl_adapter_is_open
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_probe()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Registered in the pci_driver structure, this function is called when the
 *  PCI subsystem finds a new PCI device which matches the infomation contained
 *  in the pci_device_id table.
 *
 *  PARAMETERS:
 *
 *      pdev    - a pointer to the device's pci_dev structure
 *      ent     - this device's entry in the pci_device_id table
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int __devinit wl_pci_probe( struct pci_dev *pdev,
                                const struct pci_device_id *ent )
{
    int result;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_probe" );
    DBG_ENTER( DbgInfo );
	DBG_PRINT( "%s\n", VERSION_INFO );

    result = wl_pci_setup( pdev );
    
    DBG_LEAVE( DbgInfo );

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

/*******************************************************************************
 *	wl_pci_remove()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Registered in the pci_driver structure, this function is called when the
 *  PCI subsystem detects that a PCI device which matches the infomation 
 *  contained in the pci_device_id table has been removed.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void __devexit wl_pci_remove(struct pci_dev *pdev)
{
    struct net_device       *dev = NULL;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_remove" );
    DBG_ENTER( DbgInfo );

    /* Make sure the pci_dev pointer passed in is valid */
    if( pdev == NULL ) {
        DBG_ERROR( DbgInfo, "PCI subsys passed in an invalid pci_dev pointer\n" );
        return;
    }

    dev = (struct net_device *)pci_get_drvdata( pdev );
    if( dev == NULL ) {
        DBG_ERROR( DbgInfo, "Could not retrieve net_device structure\n" );
        return;
    }

    /* Perform device cleanup */
    wl_remove( dev );
    free_irq( dev->irq, dev );

#ifdef ENABLE_DMA
    wl_pci_dma_free( pdev, (struct wl_private *)dev->priv );
#endif

    wl_device_dealloc( dev );

    DBG_LEAVE( DbgInfo );
    return;
} // wl_pci_remove
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_setup()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Called by wl_pci_probe() to begin a device's initialization process.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_setup( struct pci_dev *pdev )
{
    int                 result = 0;
    struct net_device   *dev = NULL;
    struct wl_private   *lp = NULL;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_setup" );
    DBG_ENTER( DbgInfo );

    /* Make sure the pci_dev pointer passed in is valid */
    if( pdev == NULL ) {
        DBG_ERROR( DbgInfo, "PCI subsys passed in an invalid pci_dev pointer\n" );
        return -ENODEV;
    }

    result = pci_enable_device( pdev );
    if( result != 0 ) {
        DBG_ERROR( DbgInfo, "pci_enable_device() failed\n" );
        DBG_LEAVE( DbgInfo );
        return result;
    }

    /* We found our device! Let's register it with the system */
    DBG_TRACE( DbgInfo, "Found our device, now registering\n" );
    dev = wl_device_alloc( );
    if( dev == NULL ) {
        DBG_ERROR( DbgInfo, "Could not register device!!!\n" );
        DBG_LEAVE( DbgInfo );
        return -ENOMEM;
    }

    /* Make sure that space was allocated for our private adapter struct */
    if( dev->priv == NULL ) {
        DBG_ERROR( DbgInfo, "Private adapter struct was not allocated!!!\n" );
        DBG_LEAVE( DbgInfo );
        return -ENOMEM;
    }

#ifdef ENABLE_DMA
    /* Allocate DMA Descriptors */
    if( wl_pci_dma_alloc( pdev, (struct wl_private *)dev->priv ) < 0 ) {
        DBG_ERROR( DbgInfo, "Could not allocate DMA descriptor memory!!!\n" );
        DBG_LEAVE( DbgInfo );
        return -ENOMEM;
    }
#endif

    /* Register our private adapter structure with PCI */
    pci_set_drvdata( pdev, dev );

    /* Fill out bus specific information in the net_device struct */
    dev->irq = pdev->irq;
    SET_MODULE_OWNER( dev );
    
    DBG_TRACE( DbgInfo, "Device Base Address: %#03lx\n", pdev->resource[0].start );
	dev->base_addr = pdev->resource[0].start;

    /* Initialize our device here */
    if( !wl_adapter_insert( dev )) {
        DBG_ERROR( DbgInfo, "wl_adapter_insert() FAILED!!!\n" );
        wl_device_dealloc( dev );
        DBG_LEAVE( DbgInfo );
        return -EINVAL;
    }
    
    /* Register our ISR */
    DBG_TRACE( DbgInfo, "Registering ISR...\n" );
    
    result = request_irq(dev->irq, wl_isr, SA_SHIRQ, dev->name, dev);
    if( result ) {
        DBG_WARNING( DbgInfo, "Could not register ISR!!!\n" );
        DBG_LEAVE( DbgInfo );
        return result;
	}

    /* Make sure interrupts are enabled properly for CardBus */
    lp = (struct wl_private *)dev->priv;

    if( lp->hcfCtx.IFB_BusType == CFG_NIC_BUS_TYPE_CARDBUS ||
	    lp->hcfCtx.IFB_BusType == CFG_NIC_BUS_TYPE_PCI 		) {
        DBG_TRACE( DbgInfo, "This is a PCI/CardBus card, enable interrupts\n" );
        wl_pci_enable_cardbus_interrupts( pdev );
    }

    /* Enable bus mastering */
    pci_set_master( pdev );

    DBG_LEAVE( DbgInfo );
    return 0;
} // wl_pci_setup
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_enable_cardbus_interrupts()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Called by wl_pci_setup() to enable interrupts on a CardBus device. This
 *  is done by writing bit 15 to the function event mask register. This
 *  CardBus-specific register is located in BAR2 (counting from BAR0), in memory
 *  space at byte offset 1f4 (7f4 for WARP).
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_pci_enable_cardbus_interrupts( struct pci_dev *pdev )
{
    u32                 bar2_reg;
    u32                 mem_addr_bus;
    u32                 func_evt_mask_reg;
    void                *mem_addr_kern = NULL;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_enable_cardbus_interrupts" );
    DBG_ENTER( DbgInfo );

    /* Initialize to known bad values */
    bar2_reg = 0xdeadbeef;
    mem_addr_bus = 0xdeadbeef;
    
    /* Read the BAR2 register; this register contains the base address of the
       memory region where the function event mask register lives */
    pci_read_config_dword( pdev, PCI_BASE_ADDRESS_2, &bar2_reg );
    mem_addr_bus = bar2_reg & PCI_BASE_ADDRESS_MEM_MASK;

    /* Once the base address is obtained, remap the memory region to kernel
       space so we can retrieve the register */
    mem_addr_kern = ioremap( mem_addr_bus, 0x200 );

#ifdef HERMES25
#define REG_OFFSET  0x07F4
#else
#define REG_OFFSET  0x01F4
#endif // HERMES25

#define BIT15       0x8000

    /* Retrieve the functional event mask register, enable interrupts by
       setting Bit 15, and write back the value */
    func_evt_mask_reg = *(u32 *)( mem_addr_kern + REG_OFFSET );
    func_evt_mask_reg |= BIT15;
    *(u32 *)( mem_addr_kern + REG_OFFSET ) = func_evt_mask_reg;

    /* Once complete, unmap the region and exit */
    iounmap( mem_addr_kern );

    DBG_LEAVE( DbgInfo );
    return;
} // wl_pci_enable_cardbus_interrupts
/*============================================================================*/

#ifdef ENABLE_DMA
/*******************************************************************************
 *	wl_pci_dma_alloc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates all resources needed for PCI/CardBus DMA operation
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc( struct pci_dev *pdev, struct wl_private *lp )
{
    int i;
    int status = 0;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_alloc" );
    DBG_ENTER( DbgInfo );

//     lp->dma.tx_rsc_ind = lp->dma.rx_rsc_ind = 0;
// 
//     /* Alloc for the Tx chain and its reclaim descriptor */
//     for( i = 0; i < NUM_TX_DESC; i++ ) {
//         status = wl_pci_dma_alloc_tx_packet( pdev, lp, &lp->dma.tx_packet[i] );
//         if( status == 0 ) {
//             DBG_PRINT( "lp->dma.tx_packet[%d] :                 0x%p\n", i, lp->dma.tx_packet[i] );
//             DBG_PRINT( "lp->dma.tx_packet[%d]->next_desc_addr : 0x%p\n", i, lp->dma.tx_packet[i]->next_desc_addr );
//             lp->dma.tx_rsc_ind++;
//         } else {
//             DBG_ERROR( DbgInfo, "Could not alloc DMA Tx Packet\n" );
//             break;
//         }
//     }
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc( pdev, lp, &lp->dma.tx_reclaim_desc );
//         DBG_PRINT( "lp->dma.tx_reclaim_desc: 0x%p\n", lp->dma.tx_reclaim_desc );
//     }
//     /* Alloc for the Rx chain and its reclaim descriptor */
//     if( status == 0 ) {
//         for( i = 0; i < NUM_RX_DESC; i++ ) {
//             status = wl_pci_dma_alloc_rx_packet( pdev, lp, &lp->dma.rx_packet[i] );
//             if( status == 0 ) {
//                 DBG_PRINT( "lp->dma.rx_packet[%d]                 : 0x%p\n", i, lp->dma.rx_packet[i] );
//                 DBG_PRINT( "lp->dma.rx_packet[%d]->next_desc_addr : 0x%p\n", i, lp->dma.rx_packet[i]->next_desc_addr );
//                 lp->dma.rx_rsc_ind++;
//             } else {
//                 DBG_ERROR( DbgInfo, "Could not alloc DMA Rx Packet\n" );
//                 break;
//             }
//         }
//     }
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc( pdev, lp, &lp->dma.rx_reclaim_desc );
//         DBG_PRINT( "lp->dma.rx_reclaim_desc: 0x%p\n", lp->dma.rx_reclaim_desc );
//     }
//     /* Store status, as host should not call HCF functions if this fails */
//     lp->dma.status = status;  //;?all useages of dma.status have been commented out
//     DBG_LEAVE( DbgInfo );
    return status;
} // wl_pci_dma_alloc
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Deallocated all resources needed for PCI/CardBus DMA operation
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free( struct pci_dev *pdev, struct wl_private *lp )
{
    int i;
    int status = 0;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_free" );
    DBG_ENTER( DbgInfo );

    /* Reclaim all Rx packets that were handed over to the HCF */
    /* Do I need to do this? Before this free is called, I've already disabled
       the port which will call wl_pci_dma_hcf_reclaim */
    //if( lp->dma.status == 0 )
    //{
    //    wl_pci_dma_hcf_reclaim( lp );
    //}

    /* Free everything needed for DMA Rx */
    for( i = 0; i < NUM_RX_DESC; i++ ) {
        if( lp->dma.rx_packet[i] ) {
            status = wl_pci_dma_free_rx_packet( pdev, lp, &lp->dma.rx_packet[i] );
            if( status != 0 ) {
                DBG_WARNING( DbgInfo, "Problem freeing Rx packet\n" );
            }
        }
    }
    lp->dma.rx_rsc_ind = 0;

    if( lp->dma.rx_reclaim_desc ) {
        status = wl_pci_dma_free_desc( pdev, lp, &lp->dma.rx_reclaim_desc );
        if( status != 0 ) {
            DBG_WARNING( DbgInfo, "Problem freeing Rx reclaim descriptor\n" );
        }
    }

    /* Free everything needed for DMA Tx */
    for( i = 0; i < NUM_TX_DESC; i++ ) {
        if( lp->dma.tx_packet[i] ) {
            status = wl_pci_dma_free_tx_packet( pdev, lp, &lp->dma.tx_packet[i] );
            if( status != 0 ) {
                DBG_WARNING( DbgInfo, "Problem freeing Tx packet\n" );
            }
        }
    }
    lp->dma.tx_rsc_ind = 0;

    if( lp->dma.tx_reclaim_desc ) {
        status = wl_pci_dma_free_desc( pdev, lp, &lp->dma.tx_reclaim_desc );
        if( status != 0 ) {
            DBG_WARNING( DbgInfo, "Problem freeing Tx reclaim descriptor\n" );
        }
    }

    DBG_LEAVE( DbgInfo );
    return status;
} // wl_pci_dma_free

/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_alloc_tx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates a single Tx packet, consisting of several descriptors and
 *      buffers. Data to transmit is first copied into the 'payload' buffer
 *      before being transmitted.
 *
 *  PARAMETERS:
 *
 *      pdev    - a pointer to the device's pci_dev structure
 *      lp      - the device's private adapter structure
 *      desc    - a pointer which will reference the descriptor to be alloc'd.
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc_tx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc )
{
//     int status = 0;
//     /*------------------------------------------------------------------------*/
// 
//     if( desc == NULL ) {
//         status = -EFAULT;
//     }
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc_and_buf( pdev, lp, desc,
//                                                 HCF_DMA_TX_BUF1_SIZE );
// 
//         if( status == 0 ) {
//             status = wl_pci_dma_alloc_desc_and_buf( pdev, lp,
//                                                     &( (*desc)->next_desc_addr ),
//                                                     HCF_MAX_PACKET_SIZE );
//         }
//     }
//     if( status == 0 ) {
//         (*desc)->next_desc_phys_addr = (*desc)->next_desc_addr->desc_phys_addr;
//     }
//     return status;
} // wl_pci_dma_alloc_tx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free_tx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Frees a single Tx packet, described in the corresponding alloc function.
 *
 *  PARAMETERS:
 *
 *      pdev    - a pointer to the device's pci_dev structure
 *      lp      - the device's private adapter structure
 *      desc    - a pointer which will reference the descriptor to be alloc'd.
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free_tx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc )
{
    int status = 0;
    /*------------------------------------------------------------------------*/

    if( *desc == NULL ) {
        DBG_PRINT( "Null descriptor\n" );
        status = -EFAULT;
    }
	//;?the "limited" NDIS strategy, assuming a frame consists ALWAYS out of 2
	//descriptors, make this robust
    if( status == 0 && (*desc)->next_desc_addr ) {
        status = wl_pci_dma_free_desc_and_buf( pdev, lp, &(*desc)->next_desc_addr );
    }
    if( status == 0 ) {
        status = wl_pci_dma_free_desc_and_buf( pdev, lp, desc );
    }
    return status;
} // wl_pci_dma_free_tx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_alloc_rx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates a single Rx packet, consisting of two descriptors and one
 *      contiguous buffer. THe buffer starts with the hermes-specific header.
 *      One descriptor points at the start, the other at offset 0x3a of the
 *      buffer.
 *
 *  PARAMETERS:
 *
 *      pdev    - a pointer to the device's pci_dev structure
 *      lp      - the device's private adapter structure
 *      desc    - a pointer which will reference the descriptor to be alloc'd.
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc_rx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc )
{
    int         status = 0;
    DESC_STRCT  *p;
    /*------------------------------------------------------------------------*/

//     if( desc == NULL ) {
//         status = -EFAULT;
//     }
// 	//;?the "limited" NDIS strategy, assuming a frame consists ALWAYS out of 2
// 	//descriptors, make this robust
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc( pdev, lp, desc );
// 	}
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_buf( pdev, lp, *desc, HCF_MAX_PACKET_SIZE );
//     }
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc( pdev, lp, &p );
//     }
//     if( status == 0 ) {
//         /* Size of 1st descriptor becomes 0x3a bytes */
//         SET_BUF_SIZE( *desc, HCF_DMA_RX_BUF1_SIZE );
// 
//         /* Make 2nd descriptor point at offset 0x3a of the buffer */
//         SET_BUF_SIZE( p, ( HCF_MAX_PACKET_SIZE - HCF_DMA_RX_BUF1_SIZE ));
//         p->buf_addr       = (*desc)->buf_addr + HCF_DMA_RX_BUF1_SIZE;
//         p->buf_phys_addr  = (*desc)->buf_phys_addr + HCF_DMA_RX_BUF1_SIZE;
//         p->next_desc_addr = NULL;
// 
//         /* Chain 2nd descriptor to 1st descriptor */
//         (*desc)->next_desc_addr      = p;
//         (*desc)->next_desc_phys_addr = p->desc_phys_addr;
//     }

    return status;
} // wl_pci_dma_alloc_rx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free_rx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Frees a single Rx packet, described in the corresponding alloc function.
 *
 *  PARAMETERS:
 *
 *      pdev    - a pointer to the device's pci_dev structure
 *      lp      - the device's private adapter structure
 *      desc    - a pointer which will reference the descriptor to be alloc'd.
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free_rx_packet( struct pci_dev *pdev, struct wl_private *lp,
                                DESC_STRCT **desc )
{
    int status = 0;
    DESC_STRCT *p;
    /*------------------------------------------------------------------------*/

    if( *desc == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 ) {
        p = (*desc)->next_desc_addr;

        /* Free the 2nd descriptor */
        if( p != NULL ) {
            p->buf_addr      = NULL;
            p->buf_phys_addr = 0;
            
            status = wl_pci_dma_free_desc( pdev, lp, &p );
        }
    }

    /* Free the buffer and 1st descriptor */
    if( status == 0 ) {
        SET_BUF_SIZE( *desc, HCF_MAX_PACKET_SIZE );
        status = wl_pci_dma_free_desc_and_buf( pdev, lp, desc );
    }
    return status;
} // wl_pci_dma_free_rx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_alloc_desc_and_buf()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates a DMA descriptor and buffer, and associates them with one
 *      another.
 *
 *  PARAMETERS:
 *
 *      pdev  - a pointer to the device's pci_dev structure
 *      lp    - the device's private adapter structure
 *      desc  - a pointer which will reference the descriptor to be alloc'd 
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc_desc_and_buf( struct pci_dev *pdev, struct wl_private *lp,
                                   DESC_STRCT **desc, int size )
{
    int status = 0;
    /*------------------------------------------------------------------------*/

//     if( desc == NULL ) {
//         status = -EFAULT;
//     }
//     if( status == 0 ) {
//         status = wl_pci_dma_alloc_desc( pdev, lp, desc );
// 
//         if( status == 0 ) {
//             status = wl_pci_dma_alloc_buf( pdev, lp, *desc, size );
//         }
//     }
    return status;
} // wl_pci_dma_alloc_desc_and_buf
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free_desc_and_buf()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Frees a DMA descriptor and associated buffer.
 *
 *  PARAMETERS:
 *
 *      pdev  - a pointer to the device's pci_dev structure
 *      lp    - the device's private adapter structure
 *      desc  - a pointer which will reference the descriptor to be alloc'd 
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free_desc_and_buf( struct pci_dev *pdev, struct wl_private *lp,
                                   DESC_STRCT **desc )
{
    int status = 0;
    /*------------------------------------------------------------------------*/

    if( desc == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 && *desc == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 ) {
        status = wl_pci_dma_free_buf( pdev, lp, *desc );

        if( status == 0 ) {
            status = wl_pci_dma_free_desc( pdev, lp, desc );
        }
    }
    return status;
} // wl_pci_dma_free_desc_and_buf
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_alloc_desc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates one DMA descriptor in cache coherent memory.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc_desc( struct pci_dev *pdev, struct wl_private *lp,
                           DESC_STRCT **desc )
{
//     int         status = 0;
//     dma_addr_t  pa;
//     /*------------------------------------------------------------------------*/
// 
//     DBG_FUNC( "wl_pci_dma_alloc_desc" );
//     DBG_ENTER( DbgInfo );
// 
//     if( desc == NULL ) {
//         status = -EFAULT;
//     }
//     if( status == 0 ) {
//         *desc = pci_alloc_consistent( pdev, sizeof( DESC_STRCT ), &pa );
//     }
//     if( *desc == NULL ) {
//         DBG_ERROR( DbgInfo, "pci_alloc_consistent() failed\n" );
//         status = -ENOMEM;
//     } else {
//         memset( *desc, 0, sizeof( DESC_STRCT ));
//         (*desc)->desc_phys_addr = cpu_to_le32( pa );
//     }
//     DBG_LEAVE( DbgInfo );
//     return status;
} // wl_pci_dma_alloc_desc
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free_desc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Frees one DMA descriptor in cache coherent memory.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free_desc( struct pci_dev *pdev, struct wl_private *lp,
                           DESC_STRCT **desc )
{
    int         status = 0;
    /*------------------------------------------------------------------------*/

    if( *desc == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 ) {
        pci_free_consistent( pdev, sizeof( DESC_STRCT ), *desc,
                             (*desc)->desc_phys_addr );
    }
    *desc = NULL;
    return status;
} // wl_pci_dma_free_desc
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_alloc_buf()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates one DMA buffer in cache coherent memory, and associates a DMA
 *      descriptor with this buffer.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_alloc_buf( struct pci_dev *pdev, struct wl_private *lp,
                          DESC_STRCT *desc, int size )
{
    int         status = 0;
    dma_addr_t  pa;
    /*------------------------------------------------------------------------*/

//     DBG_FUNC( "wl_pci_dma_alloc_buf" );
//     DBG_ENTER( DbgInfo );
// 
//     if( desc == NULL ) {
//         status = -EFAULT;
//     }
//     if( status == 0 && desc->buf_addr != NULL ) {
//         status = -EFAULT;
//     }
//     if( status == 0 ) {
//         desc->buf_addr = pci_alloc_consistent( pdev, size, &pa );
//     }
//     if( desc->buf_addr == NULL ) {
//         DBG_ERROR( DbgInfo, "pci_alloc_consistent() failed\n" );
//         status = -ENOMEM;
//     } else {
//         desc->buf_phys_addr = cpu_to_le32( pa );
//         SET_BUF_SIZE( desc, size );
//     }
//     DBG_LEAVE( DbgInfo );
    return status;
} // wl_pci_dma_alloc_buf
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_free_buf()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Allocates one DMA buffer in cache coherent memory, and associates a DMA
 *      descriptor with this buffer.
 *
 *  PARAMETERS:
 *
 *      pdev - a pointer to the device's pci_dev structure
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_pci_dma_free_buf( struct pci_dev *pdev, struct wl_private *lp,
                         DESC_STRCT *desc )
{
    int         status = 0;
    /*------------------------------------------------------------------------*/

    if( desc == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 && desc->buf_addr == NULL ) {
        status = -EFAULT;
    }
    if( status == 0 ) {
        pci_free_consistent( pdev, GET_BUF_SIZE( desc ), desc->buf_addr,
                             desc->buf_phys_addr );

        desc->buf_addr = 0;
        desc->buf_phys_addr = 0;
        SET_BUF_SIZE( desc, 0 );
    }
    return status;
} // wl_pci_dma_free_buf
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_hcf_supply()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Supply HCF with DMA-related resources. These consist of:
 *          - buffers and descriptors for receive purposes
 *          - one 'reclaim' descriptor for the transmit path, used to fulfill a
 *            certain H25 DMA engine requirement
 *          - one 'reclaim' descriptor for the receive path, used to fulfill a
 *            certain H25 DMA engine requirement
 *
 *      This function is called at start-of-day or at re-initialization.
 *
 *  PARAMETERS:
 *
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wl_pci_dma_hcf_supply( struct wl_private *lp )
{
    int i;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_hcf_supply" );
    DBG_ENTER( DbgInfo );

    //if( lp->dma.status == 0 );
    //{
        /* Hand over the Rx/Tx reclaim descriptors to the HCF */
        if( lp->dma.tx_reclaim_desc ) {
            DBG_PRINT( "lp->dma.tx_reclaim_desc: 0x%p\n", lp->dma.tx_reclaim_desc );
            hcf_dma_tx_put( &lp->hcfCtx, lp->dma.tx_reclaim_desc, 0 );
            lp->dma.tx_reclaim_desc = NULL;
            DBG_PRINT( "lp->dma.tx_reclaim_desc: 0x%p\n", lp->dma.tx_reclaim_desc );
        }
        if( lp->dma.rx_reclaim_desc ) {
            DBG_PRINT( "lp->dma.rx_reclaim_desc: 0x%p\n", lp->dma.rx_reclaim_desc );
            hcf_dma_rx_put( &lp->hcfCtx, lp->dma.rx_reclaim_desc );
            lp->dma.rx_reclaim_desc = NULL;
            DBG_PRINT( "lp->dma.rx_reclaim_desc: 0x%p\n", lp->dma.rx_reclaim_desc );
        }
        /* Hand over the Rx descriptor chain to the HCF */
        for( i = 0; i < NUM_RX_DESC; i++ ) {
            DBG_PRINT( "lp->dma.rx_packet[%d]:    0x%p\n", i, lp->dma.rx_packet[i] );
            hcf_dma_rx_put( &lp->hcfCtx, lp->dma.rx_packet[i] );
            lp->dma.rx_packet[i] = NULL;
            DBG_PRINT( "lp->dma.rx_packet[%d]:    0x%p\n", i, lp->dma.rx_packet[i] );
        }
    //}

    DBG_LEAVE( DbgInfo );
    return;
} // wl_pci_dma_hcf_supply
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_hcf_reclaim()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return DMA-related resources from the HCF. These consist of:
 *          - buffers and descriptors for receive purposes
 *          - buffers and descriptors for transmit purposes
 *          - one 'reclaim' descriptor for the transmit path, used to fulfill a
 *            certain H25 DMA engine requirement
 *          - one 'reclaim' descriptor for the receive path, used to fulfill a
 *            certain H25 DMA engine requirement
 *
 *      This function is called at end-of-day or at re-initialization.
 *
 *  PARAMETERS:
 *
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wl_pci_dma_hcf_reclaim( struct wl_private *lp )
{
    int i;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_hcf_reclaim" );
    DBG_ENTER( DbgInfo );

    wl_pci_dma_hcf_reclaim_rx( lp );
    for( i = 0; i < NUM_RX_DESC; i++ ) {
        DBG_PRINT( "rx_packet[%d] 0x%p\n", i, lp->dma.rx_packet[i] );
//         if( lp->dma.rx_packet[i] == NULL ) {
//             DBG_PRINT( "wl_pci_dma_hcf_reclaim: rx_packet[%d] NULL\n", i );
//         }
    }

    wl_pci_dma_hcf_reclaim_tx( lp );
    for( i = 0; i < NUM_TX_DESC; i++ ) {
        DBG_PRINT( "tx_packet[%d] 0x%p\n", i, lp->dma.tx_packet[i] );
//         if( lp->dma.tx_packet[i] == NULL ) {
//             DBG_PRINT( "wl_pci_dma_hcf_reclaim: tx_packet[%d] NULL\n", i );
//         }
     }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_pci_dma_hcf_reclaim
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_hcf_reclaim_rx()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Reclaim Rx packets that have already been processed by the HCF.
 *
 *  PARAMETERS:
 *
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wl_pci_dma_hcf_reclaim_rx( struct wl_private *lp )
{
    int         i;
    DESC_STRCT *p;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_hcf_reclaim_rx" );
    DBG_ENTER( DbgInfo );

    //if( lp->dma.status == 0 )
    //{
        while ( ( p = hcf_dma_rx_get( &lp->hcfCtx ) ) != NULL ) {
            if( p && p->buf_addr == NULL ) {
                /* A reclaim descriptor is being given back by the HCF. Reclaim
                   descriptors have a NULL buf_addr */
                lp->dma.rx_reclaim_desc = p;
            	DBG_PRINT( "reclaim_descriptor: 0x%p\n", p );
                continue;
            }
            for( i = 0; i < NUM_RX_DESC; i++ ) {
                if( lp->dma.rx_packet[i] == NULL ) {
                    break;
                }
            }
            /* An Rx buffer descriptor is being given back by the HCF */
            lp->dma.rx_packet[i] = p;
            lp->dma.rx_rsc_ind++;
        	DBG_PRINT( "rx_packet[%d] 0x%p\n", i, lp->dma.rx_packet[i] );
        }
    //}
    DBG_LEAVE( DbgInfo );
} // wl_pci_dma_hcf_reclaim_rx
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_get_tx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Obtains a Tx descriptor from the chain to use for Tx.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's wl_private structure.
 *
 *  RETURNS:
 *
 *      A pointer to the retrieved descriptor
 *
 ******************************************************************************/
DESC_STRCT * wl_pci_dma_get_tx_packet( struct wl_private *lp )
{
    int i;
    DESC_STRCT *desc = NULL;
    /*------------------------------------------------------------------------*/

    for( i = 0; i < NUM_TX_DESC; i++ ) {
        if( lp->dma.tx_packet[i] ) {
            break;
        }
    }

    if( i != NUM_TX_DESC ) {
        desc = lp->dma.tx_packet[i];

        lp->dma.tx_packet[i] = NULL;
        lp->dma.tx_rsc_ind--;

        memset( desc->buf_addr, 0, HCF_DMA_TX_BUF1_SIZE );
    }

    return desc;
} // wl_pci_dma_get_tx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_put_tx_packet()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Returns a Tx descriptor to the chain.
 *
 *  PARAMETERS:
 *
 *      lp   - a pointer to the device's wl_private structure.
 *      desc - a pointer to the descriptor to return.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_pci_dma_put_tx_packet( struct wl_private *lp, DESC_STRCT *desc )
{
    int i;
    /*------------------------------------------------------------------------*/

    for( i = 0; i < NUM_TX_DESC; i++ ) {
        if( lp->dma.tx_packet[i] == NULL ) {
            break;
        }
    }

    if( i != NUM_TX_DESC ) {
        lp->dma.tx_packet[i] = desc;
        lp->dma.tx_rsc_ind++;
    }
} // wl_pci_dma_put_tx_packet
/*============================================================================*/

/*******************************************************************************
 *	wl_pci_dma_hcf_reclaim_tx()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Reclaim Tx packets that have either been processed by the HCF due to a
 *      port disable or a Tx completion.
 *
 *  PARAMETERS:
 *
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wl_pci_dma_hcf_reclaim_tx( struct wl_private *lp )
{
    int         i;
    DESC_STRCT *p;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_pci_dma_hcf_reclaim_tx" );
    DBG_ENTER( DbgInfo );

    //if( lp->dma.status == 0 )
    //{
        while ( ( p = hcf_dma_tx_get( &lp->hcfCtx ) ) != NULL ) {

            if( p != NULL && p->buf_addr == NULL ) {
                /* A Reclaim descriptor is being given back by the HCF. Reclaim
                   descriptors have a NULL buf_addr */
                lp->dma.tx_reclaim_desc = p;
            	DBG_PRINT( "reclaim_descriptor: 0x%p\n", p );
                continue;
            }
            for( i = 0; i < NUM_TX_DESC; i++ ) {
                if( lp->dma.tx_packet[i] == NULL ) {
                    break;
                }
            }
            /* An Rx buffer descriptor is being given back by the HCF */
            lp->dma.tx_packet[i] = p;
            lp->dma.tx_rsc_ind++;
        	DBG_PRINT( "tx_packet[%d] 0x%p\n", i, lp->dma.tx_packet[i] );
        }
    //}

    if( lp->netif_queue_on == FALSE ) {
        netif_wake_queue( lp->dev );
        WL_WDS_NETIF_WAKE_QUEUE( lp );
        lp->netif_queue_on = TRUE;
    }
    DBG_LEAVE( DbgInfo );
    return;
} // wl_pci_dma_hcf_reclaim_tx
/*============================================================================*/
#endif  // ENABLE_DMA
