summaryrefslogblamecommitdiffstats
path: root/drivers/staging/wlags49_h2/wl_netdev.c
blob: a10d014365f271e6fe05a4c5c844121ad7a06dcf (plain) (tree)
























                                                                                
                                       



















                                                                                
                                                                             













                                                                                





                                                                                
                       

                         


                              




                  










                        
                        


                  
                       


                   
                    
 





                                            



                                                                                




                                                               

























                                                                                
                                   
 
                                                               
                 
                                             
 




















                                                                                
                                                        
 


                                                               



                                                              
                                                                    
 
                 
                                               
 



















                                                                                
                                                         

              




                                             
 
                                                                      
 
                      
 
                            

              




                                      
 
                                                              

              
                                                         
                                                   
                                                              
         
 


                                                                        
                           
                                      
 
                              
 
                      
                                              
 





















                                                                                


                                             
 
                            

              





                                                                          

                  
                          

      








                                                                          

                                                                            









                                                                             
                                                                            


                              

                                                            
                                       

                                                                            






                                            

                                                      











                                                                               
                                             
 



















                                                                                
                                    
 

                                             
 
                                                               
 


                                      
 

                                     
 





                                     
           
 
                            
 
                           

                                                  

              








                                                                           
 
                              
 
                 
                                              
 



                                                                                

                                                                       


                                                                    


                                                                
         
                                                    

                                            

                                        

  



















                                                                                
                                                               
 


                                             
 


                                                               
 
                            
 
                           

              










                                                                             
 


                                           
 

                                                                                 
              
                                                         

                    
                              
                                                                                             
              















                                                                              
         


                      



                              
 
                   
                                              
 


                                                                                
                                           
 


                                             
 


                                     



















                                                                                
                                          

              




                                               
 
                                                                   
 
                            

              









                                                                               

              


                                                              
 



                                                                                      
         
 


                                                                        
                           
                                      
 

                                          
 
                              
                                                   
 



















                                                                                
                                  

 




                                  

                                                                                








                                                                                




                                                                     
                             
















                                                                               
 











                                                                               
                                
                             




















                                                                              
              





                                                                             
 
                    
 

                                                                            
                                                   



                                   
 
                    
                                             
 




















                                                                                 
                                                                
 



                                             

                                                                                

                               
 





                                                                 
              



































                                                                               
                 


                                           
      






                                                                   
                                           
 





















                                                                                






                                             

                                                                                
                                                               
 
                                             

              



























                                                                            
              


















                                                                              







                                                
                                                                  
 

                                                            
 



                                                                             
              










                                                                       


                   



                                                                         
 

                                                                            

                         


                                                                                 
 
                                                      
                                                                       
              











                                                                            
                                              
                                                               
              









                                                                           
                                           
 




















                                                                                
                                         
 





                                                               
 



                                             
 
                                                               
 

                                     

       




                                                                              
 
                                                                    
 


                                                                         

                
                                             

              






























































                                                                                                




                                                                                                












                                                                                      
                    
                                                  
 



                                                                                
                                                                     
 


                                                               

                                        
                                                  
 



                                                                                

                                                    
 


                                         
 



                                      
 
                                         

                                 
                                       

      



















                                                                                
                                        
 







                                                        





                                                       







                                                                               
 
                                                               
 


                                                                             
 
                                         
 
                                         
 
                                           
 
                              
 

                                                                
 
                   
                                                     
 



















                                                                                
                                              
 



                                   
                                                       
 



















                                                                                
                                                            
 
                                          
 
                                           
                 
                                               
      
                                                  
 





















                                                                                
                                                            
 

                                           
                                                 
 



















                                                                                
                                                            
 

                                           
                                                 
 



















                                                                                
                                                            
 

                                           
                                                 
 



















                                                                                
                                                            
 

                                           
                                                 
 



















                                                                                
                                                            
 

                                           
                                                 
 



















                                                                                
                                                            
 

                                           
                                                 
 




















                                                                                
                                               
 
                  
 














                                                                                        






                                                                                         







                                                                       
 








                                                            
                                                         
 


















                                                                                
                                                 
 
                  
 

                                                         
 
                                                  
 









                                                                          
                                                           
 








                                                                                
                                      









                                                                                
                                                    
 
                  

                                                                                








                                                                           
                                                              
 








                                                                                
                                      









                                                                                
                                                   
 
                  

                                                                                








                                                                           
                                                             
 








                                                                                
                                      









                                                                                
                                                   
 
                  

                                                                                








                                                                          
                                                             
 








                                                                                
                                                                









                                                                                
                                                   
 
                  

                                                                                

                                                                 
                                                              
                                                                          

                 
                                                             
 








                                                                                
                                                                    









                                                                                
                                                    
 
                  
 


                                                                 
                                                                           

                 
 
                                                              
 

                                                                                
                    





















                                                                                
                                                                     
 


                                     

                                                                                








                                                                                
 
                                                                            
 



                                                           
 
                       
 

                                            
 




                                                    
 
                                                 


                                 
 

                                                             
 
                                         
 



                                                                        
 

                                                    
 

                                                     
 
                                               
 

                                                                    
                                   
 
                    
                                                 
 



















                                                                                
                                     
 





                                     

                                                                                
                                                               
 

                                                             

              





                                                                   



                                           
























































                                                                                                                     
              



























                                                                                 
              















                                                                           
                                                      
                                                                       
              








                                                                      
                     


                 
                                               
 
                                                                                
                       
/*******************************************************************************
 * 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 handler functions registered with the net_device
 *   structure.
 *
 *------------------------------------------------------------------------------
 *
 * 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.
 *
 ******************************************************************************/

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

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>

#include <debug.h>

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

#include <wl_if.h>
#include <wl_internal.h>
#include <wl_util.h>
#include <wl_priv.h>
#include <wl_main.h>
#include <wl_netdev.h>
#include <wl_wext.h>

#ifdef USE_PROFILE
#include <wl_profile.h>
#endif /* USE_PROFILE */

#ifdef BUS_PCMCIA
#include <wl_cs.h>
#endif /* BUS_PCMCIA */

#ifdef BUS_PCI
#include <wl_pci.h>
#endif /* BUS_PCI */

#if HCF_ENCAP
#define MTU_MAX (HCF_MAX_MSG - ETH_HLEN - 8)
#else
#define MTU_MAX (HCF_MAX_MSG - ETH_HLEN)
#endif

/*******************************************************************************
 * macros
 ******************************************************************************/
#define BLOCK_INPUT(buf, len) \
	do { \
		desc->buf_addr = buf; \
		desc->BUF_SIZE = len; \
		status = hcf_rcv_msg(&(lp->hcfCtx), desc, 0); \
	} while (0)

#define BLOCK_INPUT_DMA(buf, len) memcpy( buf, desc_next->buf_addr, pktlen )

/*******************************************************************************
 * function prototypes
 ******************************************************************************/

/*******************************************************************************
 *	wl_init()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      We never need to do anything when a "Wireless" device is "initialized"
 *  by the net software, because we only register already-found cards.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_init(struct net_device *dev)
{
	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);
	return 0;
}				/* wl_init */

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

/*******************************************************************************
 *	wl_config()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Implement the SIOCSIFMAP interface.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *      map - a pointer to the device's ifmap structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno otherwise
 *
 ******************************************************************************/
int wl_config(struct net_device *dev, struct ifmap *map)
{
	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);
	DBG_PARAM(DbgInfo, "map", "0x%p", map);

	/*
	 * The only thing we care about here is a port change.
	 * Since this not needed, ignore the request. 
	 */
	DBG_TRACE(DbgInfo, "%s: %s called.\n", dev->name, __func__);

	return 0;
}				/* wl_config */

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

/*******************************************************************************
 *	wl_stats()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return the current device statistics.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      a pointer to a net_device_stats structure containing the network
 *      statistics.
 *
 ******************************************************************************/
struct net_device_stats *wl_stats(struct net_device *dev)
{
#ifdef USE_WDS
	int count;
#endif /* USE_WDS */
	unsigned long flags;
	struct net_device_stats *pStats;
	struct wl_private *lp = wl_priv(dev);

	/*DBG_PARAM( DbgInfo, "dev", "%s (0x%p)", dev->name, dev ); */

	pStats = NULL;

	wl_lock(lp, &flags);

#ifdef USE_RTS
	if (lp->useRTS == 1) {
		wl_unlock(lp, &flags);
		return NULL;
	}
#endif /* USE_RTS */

	/* Return the statistics for the appropriate device */
#ifdef USE_WDS

	for (count = 0; count < NUM_WDS_PORTS; count++) {
		if (dev == lp->wds_port[count].dev)
			pStats = &(lp->wds_port[count].stats);
	}

#endif /* USE_WDS */

	/* If pStats is still NULL, then the device is not a WDS port */
	if (pStats == NULL)
		pStats = &(lp->stats);

	wl_unlock(lp, &flags);

	return pStats;
}				/* wl_stats */

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

/*******************************************************************************
 *	wl_open()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Open the device.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno otherwise
 *
 ******************************************************************************/
int wl_open(struct net_device *dev)
{
	int status = HCF_SUCCESS;
	struct wl_private *lp = wl_priv(dev);
	unsigned long flags;

	wl_lock(lp, &flags);

#ifdef USE_RTS
	if (lp->useRTS == 1) {
		DBG_TRACE(DbgInfo, "Skipping device open, in RTS mode\n");
		wl_unlock(lp, &flags);
		return -EIO;
	}
#endif /* USE_RTS */

#ifdef USE_PROFILE
	parse_config(dev);
#endif

	if (lp->portState == WVLAN_PORT_STATE_DISABLED) {
		DBG_TRACE(DbgInfo, "Enabling Port 0\n");
		status = wl_enable(lp);

		if (status != HCF_SUCCESS) {
			DBG_TRACE(DbgInfo, "Enable port 0 failed: 0x%x\n",
				  status);
		}
	}

	/* Holding the lock too long, make a gap to allow other processes */
	wl_unlock(lp, &flags);
	wl_lock(lp, &flags);

	if (strlen(lp->fw_image_filename)) {
		DBG_TRACE(DbgInfo, ";???? Kludgy way to force a download\n");
		status = wl_go(lp);
	} else {
		status = wl_apply(lp);
	}

	/* Holding the lock too long, make a gap to allow other processes */
	wl_unlock(lp, &flags);
	wl_lock(lp, &flags);

	/* Unsuccessful, try reset of the card to recover */
	if (status != HCF_SUCCESS)
		status = wl_reset(dev);

	/* Holding the lock too long, make a gap to allow other processes */
	wl_unlock(lp, &flags);
	wl_lock(lp, &flags);

	if (status == HCF_SUCCESS) {
		netif_carrier_on(dev);
		WL_WDS_NETIF_CARRIER_ON(lp);

		/* Start handling interrupts */
		lp->is_handling_int = WL_HANDLING_INT;
		wl_act_int_on(lp);

		netif_start_queue(dev);
		WL_WDS_NETIF_START_QUEUE(lp);
	} else {
		wl_hcf_error(dev, status);	/* Report the error */
		netif_device_detach(dev);	/* Stop the device and queue */
	}

	wl_unlock(lp, &flags);

	return status;
}				/* wl_open */

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

/*******************************************************************************
 *	wl_close()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Close the device.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno otherwise
 *
 ******************************************************************************/
int wl_close(struct net_device *dev)
{
	struct wl_private *lp = wl_priv(dev);
	unsigned long flags;

	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);

	/* Mark the adapter as busy */
	netif_stop_queue(dev);
	WL_WDS_NETIF_STOP_QUEUE(lp);

	netif_carrier_off(dev);
	WL_WDS_NETIF_CARRIER_OFF(lp);

	/*
	 * Shutdown the adapter:
	 * Disable adapter interrupts
	 * Stop Tx/Rx
	 * Update statistics
	 * Set low power mode
	 */

	wl_lock(lp, &flags);

	wl_act_int_off(lp);
	/* Stop handling interrupts */
	lp->is_handling_int = WL_NOT_HANDLING_INT;

#ifdef USE_RTS
	if (lp->useRTS == 1) {
		DBG_TRACE(DbgInfo, "Skipping device close, in RTS mode\n");
		wl_unlock(lp, &flags);
		return -EIO;
	}
#endif /* USE_RTS */

	/* Disable the ports */
	wl_disable(lp);

	wl_unlock(lp, &flags);

	return 0;
}				/* wl_close */

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

static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
	strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
	strlcpy(info->version, DRV_VERSION_STR, sizeof(info->version));

	if (dev->dev.parent) {
		dev_set_name(dev->dev.parent, "%s", info->bus_info);
	} else {
		snprintf(info->bus_info, sizeof(info->bus_info),
			 "PCMCIA FIXME");
	}
}				/* wl_get_drvinfo */

static struct ethtool_ops wl_ethtool_ops = {
	.get_drvinfo = wl_get_drvinfo,
	.get_link = ethtool_op_get_link,
};

/*******************************************************************************
 *	wl_ioctl()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The IOCTL handler for the device.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device struct.
 *      rq  - a pointer to the IOCTL request buffer.
 *      cmd - the IOCTL command code.
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct wl_private *lp = wl_priv(dev);
	unsigned long flags;
	int ret = 0;

	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);
	DBG_PARAM(DbgInfo, "rq", "0x%p", rq);
	DBG_PARAM(DbgInfo, "cmd", "0x%04x", cmd);

	wl_lock(lp, &flags);

	wl_act_int_off(lp);

#ifdef USE_RTS
	if (lp->useRTS == 1) {
		/* Handle any RTS IOCTL here */
		if (cmd == WL_IOCTL_RTS) {
			DBG_TRACE(DbgInfo, "IOCTL: WL_IOCTL_RTS\n");
			ret = wvlan_rts((struct rtsreq *)rq, dev->base_addr);
		} else {
			DBG_TRACE(DbgInfo,
				  "IOCTL not supported in RTS mode: 0x%X\n",
				  cmd);
			ret = -EOPNOTSUPP;
		}

		goto out_act_int_on_unlock;
	}
#endif /* USE_RTS */

	/* Only handle UIL IOCTL requests when the UIL has the system blocked. */
	if (!((lp->flags & WVLAN2_UIL_BUSY) && (cmd != WVLAN2_IOCTL_UIL))) {
#ifdef USE_UIL
		struct uilreq *urq = (struct uilreq *)rq;
#endif /* USE_UIL */

		switch (cmd) {
			/* ================== Private IOCTLs (up to 16) ================== */
#ifdef USE_UIL
		case WVLAN2_IOCTL_UIL:
			DBG_TRACE(DbgInfo, "IOCTL: WVLAN2_IOCTL_UIL\n");
			ret = wvlan_uil(urq, lp);
			break;
#endif /* USE_UIL */

		default:
			DBG_TRACE(DbgInfo, "IOCTL CODE NOT SUPPORTED: 0x%X\n",
				  cmd);
			ret = -EOPNOTSUPP;
			break;
		}
	} else {
		DBG_WARNING(DbgInfo,
			    "DEVICE IS BUSY, CANNOT PROCESS REQUEST\n");
		ret = -EBUSY;
	}

#ifdef USE_RTS
out_act_int_on_unlock:
#endif /* USE_RTS */
	wl_act_int_on(lp);

	wl_unlock(lp, &flags);

	return ret;
}				/* wl_ioctl */

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

#ifdef CONFIG_NET_POLL_CONTROLLER
static void wl_poll(struct net_device *dev)
{
	struct wl_private *lp = wl_priv(dev);
	unsigned long flags;
	struct pt_regs regs;

	wl_lock(lp, &flags);
	wl_isr(dev->irq, dev, &regs);
	wl_unlock(lp, &flags);
}
#endif

/*******************************************************************************
 *	wl_tx_timeout()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler called when, for some reason, a Tx request is not completed.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device struct.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_tx_timeout(struct net_device *dev)
{
#ifdef USE_WDS
	int count;
#endif /* USE_WDS */
	unsigned long flags;
	struct wl_private *lp = wl_priv(dev);
	struct net_device_stats *pStats = NULL;

	DBG_WARNING(DbgInfo, "%s: Transmit timeout.\n", dev->name);

	wl_lock(lp, &flags);

#ifdef USE_RTS
	if (lp->useRTS == 1) {
		DBG_TRACE(DbgInfo,
			  "Skipping tx_timeout handler, in RTS mode\n");
		wl_unlock(lp, &flags);
		return;
	}
#endif /* USE_RTS */

	/* Figure out which device (the "root" device or WDS port) this timeout
	   is for */
#ifdef USE_WDS

	for (count = 0; count < NUM_WDS_PORTS; count++) {
		if (dev == lp->wds_port[count].dev) {
			pStats = &(lp->wds_port[count].stats);

			/* Break the loop so that we can use the counter to access WDS
			   information in the private structure */
			break;
		}
	}

#endif /* USE_WDS */

	/* If pStats is still NULL, then the device is not a WDS port */
	if (pStats == NULL)
		pStats = &(lp->stats);

	/* Accumulate the timeout error */
	pStats->tx_errors++;

	wl_unlock(lp, &flags);
}				/* wl_tx_timeout */

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

/*******************************************************************************
 *	wl_send()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The routine which performs data transmits.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's wl_private struct.
 *
 *  RETURNS:
 *
 *      0 on success
 *      1 on error
 *
 ******************************************************************************/
int wl_send(struct wl_private *lp)
{

	int status;
	DESC_STRCT *desc;
	WVLAN_LFRAME *txF = NULL;
	struct list_head *element;
	int len;
    /*------------------------------------------------------------------------*/

	if (lp == NULL) {
		DBG_ERROR(DbgInfo, "Private adapter struct is NULL\n");
		return FALSE;
	}
	if (lp->dev == NULL) {
		DBG_ERROR(DbgInfo, "net_device struct in wl_private is NULL\n");
		return FALSE;
	}

	/*
	 * Check for the availability of FIDs; if none are available,
	 * don't take any frames off the txQ
	 */
	if (lp->hcfCtx.IFB_RscInd == 0)
		return FALSE;

	/* Reclaim the TxQ Elements and place them back on the free queue */
	if (!list_empty(&(lp->txQ[0]))) {
		element = lp->txQ[0].next;

		txF = (WVLAN_LFRAME *) list_entry(element, WVLAN_LFRAME, node);
		if (txF != NULL) {
			lp->txF.skb = txF->frame.skb;
			lp->txF.port = txF->frame.port;

			txF->frame.skb = NULL;
			txF->frame.port = 0;

			list_del(&(txF->node));
			list_add(element, &(lp->txFree));

			lp->txQ_count--;

			if (lp->txQ_count < TX_Q_LOW_WATER_MARK) {
				if (lp->netif_queue_on == FALSE) {
					DBG_TX(DbgInfo, "Kickstarting Q: %d\n",
					       lp->txQ_count);
					netif_wake_queue(lp->dev);
					WL_WDS_NETIF_WAKE_QUEUE(lp);
					lp->netif_queue_on = TRUE;
				}
			}
		}
	}

	if (lp->txF.skb == NULL)
		return FALSE;

	/* If the device has resources (FIDs) available, then Tx the packet */
	/* Format the TxRequest and send it to the adapter */
	len = lp->txF.skb->len < ETH_ZLEN ? ETH_ZLEN : lp->txF.skb->len;

	desc = &(lp->desc_tx);
	desc->buf_addr = lp->txF.skb->data;
	desc->BUF_CNT = len;
	desc->next_desc_addr = NULL;

	status = hcf_send_msg(&(lp->hcfCtx), desc, lp->txF.port);

	if (status == HCF_SUCCESS) {
		lp->dev->trans_start = jiffies;

		DBG_TX(DbgInfo, "Transmit...\n");

		if (lp->txF.port == HCF_PORT_0) {
			lp->stats.tx_packets++;
			lp->stats.tx_bytes += lp->txF.skb->len;
		}
#ifdef USE_WDS
		else {
			lp->wds_port[((lp->txF.port >> 8) -
				      1)].stats.tx_packets++;
			lp->wds_port[((lp->txF.port >> 8) -
				      1)].stats.tx_bytes += lp->txF.skb->len;
		}

#endif /* USE_WDS */

		/* Free the skb and perform queue cleanup, as the buffer was
		   transmitted successfully */
		dev_consume_skb_any( lp->txF.skb );

		lp->txF.skb = NULL;
		lp->txF.port = 0;
	}

	return TRUE;
}				/* wl_send */

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

/*******************************************************************************
 *	wl_tx()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The Tx handler function for the network layer.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff structure containing the data to transfer.
 *      dev - a pointer to the device's net_device structure.
 *
 *  RETURNS:
 *
 *      0 on success
 *      1 on error
 *
 ******************************************************************************/
int wl_tx(struct sk_buff *skb, struct net_device *dev, int port)
{
	unsigned long flags;
	struct wl_private *lp = wl_priv(dev);
	WVLAN_LFRAME *txF = NULL;
	struct list_head *element;
    /*------------------------------------------------------------------------*/

	/* Grab the spinlock */
	wl_lock(lp, &flags);

	if (lp->flags & WVLAN2_UIL_BUSY) {
		DBG_WARNING(DbgInfo, "UIL has device blocked\n");
		/* Start dropping packets here??? */
		wl_unlock(lp, &flags);
		return 1;
	}
#ifdef USE_RTS
	if (lp->useRTS == 1) {
		DBG_PRINT("RTS: we're getting a Tx...\n");
		wl_unlock(lp, &flags);
		return 1;
	}
#endif /* USE_RTS */

	if (!lp->use_dma) {
		/* Get an element from the queue */
		element = lp->txFree.next;
		txF = (WVLAN_LFRAME *) list_entry(element, WVLAN_LFRAME, node);
		if (txF == NULL) {
			DBG_ERROR(DbgInfo, "Problem with list_entry\n");
			wl_unlock(lp, &flags);
			return 1;
		}
		/* Fill out the frame */
		txF->frame.skb = skb;
		txF->frame.port = port;
		/* Move the frame to the txQ */
		/* NOTE: Here's where we would do priority queueing */
		list_move(&(txF->node), &(lp->txQ[0]));

		lp->txQ_count++;
		if (lp->txQ_count >= DEFAULT_NUM_TX_FRAMES) {
			DBG_TX(DbgInfo, "Q Full: %d\n", lp->txQ_count);
			if (lp->netif_queue_on == TRUE) {
				netif_stop_queue(lp->dev);
				WL_WDS_NETIF_STOP_QUEUE(lp);
				lp->netif_queue_on = FALSE;
			}
		}
	}
	wl_act_int_off(lp);	/* Disable Interrupts */

	/* Send the data to the hardware using the appropriate method */
#ifdef ENABLE_DMA
	if (lp->use_dma) {
		wl_send_dma(lp, skb, port);
	} else
#endif
	{
		wl_send(lp);
	}
	/* Re-enable Interrupts, release the spinlock and return */
	wl_act_int_on(lp);
	wl_unlock(lp, &flags);
	return 0;
}				/* wl_tx */

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

/*******************************************************************************
 *	wl_rx()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The routine which performs data reception.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure.
 *
 *  RETURNS:
 *
 *      0 on success
 *      1 on error
 *
 ******************************************************************************/
int wl_rx(struct net_device *dev)
{
	int port;
	struct sk_buff *skb;
	struct wl_private *lp = wl_priv(dev);
	int status;
	hcf_16 pktlen;
	hcf_16 hfs_stat;
	DESC_STRCT *desc;
    /*------------------------------------------------------------------------*/

	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);

	if (!(lp->flags & WVLAN2_UIL_BUSY)) {

#ifdef USE_RTS
		if (lp->useRTS == 1) {
			DBG_PRINT("RTS: We're getting an Rx...\n");
			return -EIO;
		}
#endif /* USE_RTS */

		/* Read the HFS_STAT register from the lookahead buffer */
		hfs_stat = (hcf_16) ((lp->lookAheadBuf[HFS_STAT]) |
				     (lp->lookAheadBuf[HFS_STAT + 1] << 8));

		/* Make sure the frame isn't bad */
		if ((hfs_stat & HFS_STAT_ERR) != HCF_SUCCESS) {
			DBG_WARNING(DbgInfo,
				    "HFS_STAT_ERROR (0x%x) in Rx Packet\n",
				    lp->lookAheadBuf[HFS_STAT]);
			return -EIO;
		}

		/* Determine what port this packet is for */
		port = (hfs_stat >> 8) & 0x0007;
		DBG_RX(DbgInfo, "Rx frame for port %d\n", port);

		pktlen = lp->hcfCtx.IFB_RxLen;
		if (pktlen != 0) {
			skb = ALLOC_SKB(pktlen);
			if (skb != NULL) {
				/* Set the netdev based on the port */
				switch (port) {
#ifdef USE_WDS
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
					skb->dev = lp->wds_port[port - 1].dev;
					break;
#endif /* USE_WDS */

				case 0:
				default:
					skb->dev = dev;
					break;
				}

				desc = &(lp->desc_rx);

				desc->next_desc_addr = NULL;

/*
#define BLOCK_INPUT(buf, len) \
    desc->buf_addr = buf; \
    desc->BUF_SIZE = len; \
    status = hcf_rcv_msg(&(lp->hcfCtx), desc, 0)
*/

				GET_PACKET(skb->dev, skb, pktlen);

				if (status == HCF_SUCCESS) {
					netif_rx(skb);

					if (port == 0) {
						lp->stats.rx_packets++;
						lp->stats.rx_bytes += pktlen;
					}
#ifdef USE_WDS
					else {
						lp->wds_port[port -
							     1].stats.
						    rx_packets++;
						lp->wds_port[port -
							     1].stats.
						    rx_bytes += pktlen;
					}
#endif /* USE_WDS */

					dev->last_rx = jiffies;

#ifdef WIRELESS_EXT
#ifdef WIRELESS_SPY
					if (lp->spydata.spy_number > 0) {
						char *srcaddr =
						    skb->mac.raw +
						    MAC_ADDR_SIZE;

						wl_spy_gather(dev, srcaddr);
					}
#endif /* WIRELESS_SPY */
#endif /* WIRELESS_EXT */
				} else {
					DBG_ERROR(DbgInfo,
						  "Rx request to card FAILED\n");

					if (port == 0)
						lp->stats.rx_dropped++;
#ifdef USE_WDS
					else {
						lp->wds_port[port -
							     1].stats.
						    rx_dropped++;
					}
#endif /* USE_WDS */

					dev_kfree_skb(skb);
				}
			} else {
				DBG_ERROR(DbgInfo, "Could not alloc skb\n");

				if (port == 0)
					lp->stats.rx_dropped++;
#ifdef USE_WDS
				else {
					lp->wds_port[port -
						     1].stats.rx_dropped++;
				}
#endif /* USE_WDS */
			}
		}
	}

	return 0;
}				/* wl_rx */

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

/*******************************************************************************
 *	wl_multicast()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Function to handle multicast packets
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
#ifdef NEW_MULTICAST

void wl_multicast(struct net_device *dev)
{
#if 1				/* (HCF_TYPE) & HCF_TYPE_STA */
	/*
	 * should we return an error status in AP mode ?
	 * seems reasonable that even an AP-only driver
	 * could afford this small additional footprint
	 */

	int x;
	struct netdev_hw_addr *ha;
	struct wl_private *lp = wl_priv(dev);
	unsigned long flags;

	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);

	if (!wl_adapter_is_open(dev))
		return;

#if DBG
	if (DBG_FLAGS(DbgInfo) & DBG_PARAM_ON) {
		DBG_PRINT("  flags: %s%s%s\n",
			  (dev->flags & IFF_PROMISC) ? "Promiscuous " : "",
			  (dev->flags & IFF_MULTICAST) ? "Multicast " : "",
			  (dev->flags & IFF_ALLMULTI) ? "All-Multicast" : "");

		DBG_PRINT("  mc_count: %d\n", netdev_mc_count(dev));

		netdev_for_each_mc_addr(ha, dev)
		    DBG_PRINT("    %pM (%d)\n", ha->addr, dev->addr_len);
	}
#endif /* DBG */

	if (!(lp->flags & WVLAN2_UIL_BUSY)) {

#ifdef USE_RTS
		if (lp->useRTS == 1) {
			DBG_TRACE(DbgInfo, "Skipping multicast, in RTS mode\n");
			return;
		}
#endif /* USE_RTS */

		wl_lock(lp, &flags);
		wl_act_int_off(lp);

		if (CNV_INT_TO_LITTLE(lp->hcfCtx.IFB_FWIdentity.comp_id) ==
		    COMP_ID_FW_STA) {
			if (dev->flags & IFF_PROMISC) {
				/* Enable promiscuous mode */
				lp->ltvRecord.len = 2;
				lp->ltvRecord.typ = CFG_PROMISCUOUS_MODE;
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE(1);
				DBG_PRINT
				    ("Enabling Promiscuous mode (IFF_PROMISC)\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));
			} else if ((netdev_mc_count(dev) > HCF_MAX_MULTICAST)
				   || (dev->flags & IFF_ALLMULTI)) {
				/* Shutting off this filter will enable all multicast frames to
				   be sent up from the device; however, this is a static RID, so
				   a call to wl_apply() is needed */
				lp->ltvRecord.len = 2;
				lp->ltvRecord.typ = CFG_CNF_RX_ALL_GROUP_ADDR;
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE(0);
				DBG_PRINT
				    ("Enabling all multicast mode (IFF_ALLMULTI)\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));
				wl_apply(lp);
			} else if (!netdev_mc_empty(dev)) {
				/* Set the multicast addresses */
				lp->ltvRecord.len =
				    (netdev_mc_count(dev) * 3) + 1;
				lp->ltvRecord.typ = CFG_GROUP_ADDR;

				x = 0;
				netdev_for_each_mc_addr(ha, dev)
				    memcpy(&
					   (lp->ltvRecord.u.u8[x++ * ETH_ALEN]),
					   ha->addr, ETH_ALEN);
				DBG_PRINT("Setting multicast list\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));
			} else {
				/* Disable promiscuous mode */
				lp->ltvRecord.len = 2;
				lp->ltvRecord.typ = CFG_PROMISCUOUS_MODE;
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE(0);
				DBG_PRINT("Disabling Promiscuous mode\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));

				/* Disable multicast mode */
				lp->ltvRecord.len = 2;
				lp->ltvRecord.typ = CFG_GROUP_ADDR;
				DBG_PRINT("Disabling Multicast mode\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));

				/*
				 * Turning on this filter will prevent all multicast frames from
				 * being sent up from the device; however, this is a static RID,
				 * so a call to wl_apply() is needed
				 */
				lp->ltvRecord.len = 2;
				lp->ltvRecord.typ = CFG_CNF_RX_ALL_GROUP_ADDR;
				lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE(1);
				DBG_PRINT
				    ("Disabling all multicast mode (IFF_ALLMULTI)\n");
				hcf_put_info(&(lp->hcfCtx),
					     (LTVP) & (lp->ltvRecord));
				wl_apply(lp);
			}
		}
		wl_act_int_on(lp);
		wl_unlock(lp, &flags);
	}
#endif /* HCF_STA */
}				/* wl_multicast */

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

#else /* NEW_MULTICAST */

void wl_multicast(struct net_device *dev, int num_addrs, void *addrs)
{
	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);
	DBG_PARAM(DbgInfo, "num_addrs", "%d", num_addrs);
	DBG_PARAM(DbgInfo, "addrs", "0x%p", addrs);

#error Obsolete set multicast interface!
}				/* wl_multicast */

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

#endif /* NEW_MULTICAST */

static const struct net_device_ops wl_netdev_ops = {
	.ndo_start_xmit = &wl_tx_port0,

	.ndo_set_config = &wl_config,
	.ndo_get_stats = &wl_stats,
	.ndo_set_rx_mode = &wl_multicast,

	.ndo_init = &wl_insert,
	.ndo_open = &wl_adapter_open,
	.ndo_stop = &wl_adapter_close,
	.ndo_do_ioctl = &wl_ioctl,

	.ndo_tx_timeout = &wl_tx_timeout,

#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller = wl_poll,
#endif
};

/*******************************************************************************
 *	wl_device_alloc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Create instances of net_device and wl_private for the new adapter
 *  and register the device's entry points in the net_device structure.
 *
 *  PARAMETERS:
 *
 *      N/A
 *
 *  RETURNS:
 *
 *      a pointer to an allocated and initialized net_device struct for this
 *      device.
 *
 ******************************************************************************/
struct net_device *wl_device_alloc(void)
{
	struct net_device *dev = NULL;
	struct wl_private *lp = NULL;

	/* Alloc a net_device struct */
	dev = alloc_etherdev(sizeof(struct wl_private));
	if (!dev)
		return NULL;

	/*
	 * Initialize the 'next' pointer in the struct.
	 * Currently only used for PCI,
	 * but do it here just in case it's used
	 * for other buses in the future
	 */
	lp = wl_priv(dev);

	/* Check MTU */
	if (dev->mtu > MTU_MAX) {
		DBG_WARNING(DbgInfo, "%s: MTU set too high, limiting to %d.\n",
			    dev->name, MTU_MAX);
		dev->mtu = MTU_MAX;
	}

	/* Setup the function table in the device structure. */

	dev->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
	lp->wireless_data.spy_data = &lp->spy_data;
	dev->wireless_data = &lp->wireless_data;

	dev->netdev_ops = &wl_netdev_ops;

	dev->watchdog_timeo = TX_TIMEOUT;

	dev->ethtool_ops = &wl_ethtool_ops;

	netif_stop_queue(dev);

	/* Allocate virtual devices for WDS support if needed */
	WL_WDS_DEVICE_ALLOC(lp);

	return dev;
}				/* wl_device_alloc */

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

/*******************************************************************************
 *	wl_device_dealloc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Free instances of net_device and wl_private strcutres for an adapter
 *  and perform basic cleanup.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_device_dealloc(struct net_device *dev)
{
	/* Dealloc the WDS ports */
	WL_WDS_DEVICE_DEALLOC(lp);

	free_netdev(dev);
}				/* wl_device_dealloc */

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

/*******************************************************************************
 *	wl_tx_port0()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_0.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_0.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port0(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 0\n");

	return wl_tx(skb, dev, HCF_PORT_0);
#ifdef ENABLE_DMA
	return wl_tx_dma(skb, dev, HCF_PORT_0);
#endif
}				/* wl_tx_port0i */

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

#ifdef USE_WDS

/*******************************************************************************
 *	wl_tx_port1()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_1.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_1.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port1(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 1\n");
	return wl_tx(skb, dev, HCF_PORT_1);
}				/* wl_tx_port1 */

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

/*******************************************************************************
 *	wl_tx_port2()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_2.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_2.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port2(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 2\n");
	return wl_tx(skb, dev, HCF_PORT_2);
}				/* wl_tx_port2 */

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

/*******************************************************************************
 *	wl_tx_port3()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_3.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_3.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port3(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 3\n");
	return wl_tx(skb, dev, HCF_PORT_3);
}				/* wl_tx_port3 */

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

/*******************************************************************************
 *	wl_tx_port4()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_4.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_4.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port4(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 4\n");
	return wl_tx(skb, dev, HCF_PORT_4);
}				/* wl_tx_port4 */

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

/*******************************************************************************
 *	wl_tx_port5()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_5.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_5.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port5(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 5\n");
	return wl_tx(skb, dev, HCF_PORT_5);
}				/* wl_tx_port5 */

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

/*******************************************************************************
 *	wl_tx_port6()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The handler routine for Tx over HCF_PORT_6.
 *
 *  PARAMETERS:
 *
 *      skb - a pointer to the sk_buff to transmit.
 *      dev - a pointer to a net_device structure representing HCF_PORT_6.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
int wl_tx_port6(struct sk_buff *skb, struct net_device *dev)
{
	DBG_TX(DbgInfo, "Tx on Port 6\n");
	return wl_tx(skb, dev, HCF_PORT_6);
}				/* wl_tx_port6 */

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

/*******************************************************************************
 *	wl_wds_device_alloc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Create instances of net_device to represent the WDS ports, and register
 *  the device's entry points in the net_device structure.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A, but will place pointers to the allocated and initialized net_device
 *      structs in the private adapter structure.
 *
 ******************************************************************************/
void wl_wds_device_alloc(struct wl_private *lp)
{
	int count;

	/* WDS support requires additional net_device structs to be allocated,
	   so that user space apps can use these virtual devices to specify the
	   port on which to Tx/Rx */
	for (count = 0; count < NUM_WDS_PORTS; count++) {
		struct net_device *dev_wds = NULL;

		dev_wds = kzalloc(sizeof(struct net_device), GFP_KERNEL);
		if (!dev_wds)
			return;

		ether_setup(dev_wds);

		lp->wds_port[count].dev = dev_wds;

		/* Re-use wl_init for all the devices, as it currently does nothing, but
		 * is required. Re-use the stats/tx_timeout handler for all as well; the
		 * WDS port which is requesting these operations can be determined by
		 * the net_device pointer. Set the private member of all devices to point
		 * to the same net_device struct; that way, all information gets
		 * funnelled through the one "real" net_device. Name the WDS ports
		 * "wds<n>"
		 * */
		lp->wds_port[count].dev->init = &wl_init;
		lp->wds_port[count].dev->get_stats = &wl_stats;
		lp->wds_port[count].dev->tx_timeout = &wl_tx_timeout;
		lp->wds_port[count].dev->watchdog_timeo = TX_TIMEOUT;
		lp->wds_port[count].dev->priv = lp;

		sprintf(lp->wds_port[count].dev->name, "wds%d", count);
	}

	/* Register the Tx handlers */
	lp->wds_port[0].dev->hard_start_xmit = &wl_tx_port1;
	lp->wds_port[1].dev->hard_start_xmit = &wl_tx_port2;
	lp->wds_port[2].dev->hard_start_xmit = &wl_tx_port3;
	lp->wds_port[3].dev->hard_start_xmit = &wl_tx_port4;
	lp->wds_port[4].dev->hard_start_xmit = &wl_tx_port5;
	lp->wds_port[5].dev->hard_start_xmit = &wl_tx_port6;

	WL_WDS_NETIF_STOP_QUEUE(lp);
}				/* wl_wds_device_alloc */

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

/*******************************************************************************
 *	wl_wds_device_dealloc()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Free instances of net_device structures used to support WDS.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_device_dealloc(struct wl_private *lp)
{
	int count;

	for (count = 0; count < NUM_WDS_PORTS; count++) {
		struct net_device *dev_wds = NULL;

		dev_wds = lp->wds_port[count].dev;

		if (dev_wds != NULL) {
			if (dev_wds->flags & IFF_UP) {
				dev_close(dev_wds);
				dev_wds->flags &= ~(IFF_UP | IFF_RUNNING);
			}

			free_netdev(dev_wds);
			lp->wds_port[count].dev = NULL;
		}
	}
}				/* wl_wds_device_dealloc */

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

/*******************************************************************************
 *	wl_wds_netif_start_queue()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Used to start the netif queues of all the "virtual" network devices
 *      which represent the WDS ports.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_netif_start_queue(struct wl_private *lp)
{
	int count;
    /*------------------------------------------------------------------------*/

	if (lp != NULL) {
		for (count = 0; count < NUM_WDS_PORTS; count++) {
			if (lp->wds_port[count].is_registered &&
			    lp->wds_port[count].netif_queue_on == FALSE) {
				netif_start_queue(lp->wds_port[count].dev);
				lp->wds_port[count].netif_queue_on = TRUE;
			}
		}
	}
}				/* wl_wds_netif_start_queue */

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

/*******************************************************************************
 *	wl_wds_netif_stop_queue()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Used to stop the netif queues of all the "virtual" network devices
 *      which represent the WDS ports.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_netif_stop_queue(struct wl_private *lp)
{
	int count;
    /*------------------------------------------------------------------------*/

	if (lp != NULL) {
		for (count = 0; count < NUM_WDS_PORTS; count++) {
			if (lp->wds_port[count].is_registered &&
			    lp->wds_port[count].netif_queue_on == TRUE) {
				netif_stop_queue(lp->wds_port[count].dev);
				lp->wds_port[count].netif_queue_on = FALSE;
			}
		}
	}
}				/* wl_wds_netif_stop_queue */

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

/*******************************************************************************
 *	wl_wds_netif_wake_queue()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Used to wake the netif queues of all the "virtual" network devices
 *      which represent the WDS ports.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_netif_wake_queue(struct wl_private *lp)
{
	int count;
    /*------------------------------------------------------------------------*/

	if (lp != NULL) {
		for (count = 0; count < NUM_WDS_PORTS; count++) {
			if (lp->wds_port[count].is_registered &&
			    lp->wds_port[count].netif_queue_on == FALSE) {
				netif_wake_queue(lp->wds_port[count].dev);
				lp->wds_port[count].netif_queue_on = TRUE;
			}
		}
	}
}				/* wl_wds_netif_wake_queue */

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

/*******************************************************************************
 *	wl_wds_netif_carrier_on()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Used to signal the network layer that carrier is present on all of the
 *      "virtual" network devices which represent the WDS ports.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_netif_carrier_on(struct wl_private *lp)
{
	int count;
    /*------------------------------------------------------------------------*/

	if (lp != NULL) {
		for (count = 0; count < NUM_WDS_PORTS; count++) {
			if (lp->wds_port[count].is_registered)
				netif_carrier_on(lp->wds_port[count].dev);
		}
	}
}				/* wl_wds_netif_carrier_on */

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

/*******************************************************************************
 *	wl_wds_netif_carrier_off()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Used to signal the network layer that carrier is NOT present on all of
 *      the "virtual" network devices which represent the WDS ports.
 *
 *  PARAMETERS:
 *
 *      lp  - a pointer to the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wds_netif_carrier_off(struct wl_private *lp)
{
	int count;

	if (lp != NULL) {
		for (count = 0; count < NUM_WDS_PORTS; count++) {
			if (lp->wds_port[count].is_registered)
				netif_carrier_off(lp->wds_port[count].dev);
		}
	}

}				/* wl_wds_netif_carrier_off */

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

#endif /* USE_WDS */

#ifdef ENABLE_DMA
/*******************************************************************************
 *	wl_send_dma()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The routine which performs data transmits when using busmaster DMA.
 *
 *  PARAMETERS:
 *
 *      lp   - a pointer to the device's wl_private struct.
 *      skb  - a pointer to the network layer's data buffer.
 *      port - the Hermes port on which to transmit.
 *
 *  RETURNS:
 *
 *      0 on success
 *      1 on error
 *
 ******************************************************************************/
int wl_send_dma(struct wl_private *lp, struct sk_buff *skb, int port)
{
	int len;
	DESC_STRCT *desc = NULL;
	DESC_STRCT *desc_next = NULL;
    /*------------------------------------------------------------------------*/

	if (lp == NULL) {
		DBG_ERROR(DbgInfo, "Private adapter struct is NULL\n");
		return FALSE;
	}

	if (lp->dev == NULL) {
		DBG_ERROR(DbgInfo, "net_device struct in wl_private is NULL\n");
		return FALSE;
	}

	/* AGAIN, ALL THE QUEUEING DONE HERE IN I/O MODE IS NOT PERFORMED */

	if (skb == NULL) {
		DBG_WARNING(DbgInfo, "Nothing to send.\n");
		return FALSE;
	}

	len = skb->len;

	/* Get a free descriptor */
	desc = wl_pci_dma_get_tx_packet(lp);

	if (desc == NULL) {
		if (lp->netif_queue_on == TRUE) {
			netif_stop_queue(lp->dev);
			WL_WDS_NETIF_STOP_QUEUE(lp);
			lp->netif_queue_on = FALSE;

			dev_kfree_skb_any( skb );
			return 0;
		}
	}

	SET_BUF_CNT(desc, /*HCF_DMA_FD_CNT */ HFS_ADDR_DEST);
	SET_BUF_SIZE(desc, HCF_DMA_TX_BUF1_SIZE);

	desc_next = desc->next_desc_addr;

	if (desc_next->buf_addr == NULL) {
		DBG_ERROR(DbgInfo, "DMA descriptor buf_addr is NULL\n");
		return FALSE;
	}

	/* Copy the payload into the DMA packet */
	memcpy(desc_next->buf_addr, skb->data, len);

	SET_BUF_CNT(desc_next, len);
	SET_BUF_SIZE(desc_next, HCF_MAX_PACKET_SIZE);

	hcf_dma_tx_put(&(lp->hcfCtx), desc, 0);

	/* Free the skb and perform queue cleanup, as the buffer was
	   transmitted successfully */
	dev_consume_skb_any( skb );

	return TRUE;
}				/* wl_send_dma */

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

/*******************************************************************************
 *	wl_rx_dma()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      The routine which performs data reception when using busmaster DMA.
 *
 *  PARAMETERS:
 *
 *      dev - a pointer to the device's net_device structure.
 *
 *  RETURNS:
 *
 *      0 on success
 *      1 on error
 *
 ******************************************************************************/
int wl_rx_dma(struct net_device *dev)
{
	int port;
	hcf_16 pktlen;
	hcf_16 hfs_stat;
	struct sk_buff *skb;
	struct wl_private *lp = NULL;
	DESC_STRCT *desc, *desc_next;
    /*------------------------------------------------------------------------*/

	DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);

	lp = dev->priv;
	if ((lp != NULL) && !(lp->flags & WVLAN2_UIL_BUSY)) {

#ifdef USE_RTS
		if (lp->useRTS == 1) {
			DBG_PRINT("RTS: We're getting an Rx...\n");
			return -EIO;
		}
#endif /* USE_RTS */

		/*
		 *if( lp->dma.status == 0 )
		 *{
		 */
		desc = hcf_dma_rx_get(&(lp->hcfCtx));

		if (desc != NULL) {
			/* Check and see if we rcvd. a WMP frame */
			/*
			   if((( *(hcf_8 *)&desc->buf_addr[HFS_STAT] ) &
			   ( HFS_STAT_MSG_TYPE | HFS_STAT_ERR )) == HFS_STAT_WMP_MSG )
			   {
			   DBG_TRACE( DbgInfo, "Got a WMP frame\n" );

			   x.len = sizeof( CFG_MB_INFO_RANGE2_STRCT ) / sizeof( hcf_16 );
			   x.typ = CFG_MB_INFO;
			   x.base_typ = CFG_WMP;
			   x.frag_cnt = 2;
			   x.frag_buf[0].frag_len  = GET_BUF_CNT( descp ) / sizeof( hcf_16 );
			   x.frag_buf[0].frag_addr = (hcf_8 *) descp->buf_addr ;
			   x.frag_buf[1].frag_len  = ( GET_BUF_CNT( descp->next_desc_addr ) + 1 ) / sizeof( hcf_16 );
			   x.frag_buf[1].frag_addr = (hcf_8 *) descp->next_desc_addr->buf_addr ;

			   hcf_put_info( &( lp->hcfCtx ), (LTVP)&x );
			   }
			 */

			desc_next = desc->next_desc_addr;

			/* Make sure the buffer isn't empty */
			if (GET_BUF_CNT(desc) == 0) {
				DBG_WARNING(DbgInfo, "Buffer is empty!\n");

				/* Give the descriptor back to the HCF */
				hcf_dma_rx_put(&(lp->hcfCtx), desc);
				return -EIO;
			}

			/* Read the HFS_STAT register from the lookahead buffer */
			hfs_stat = (hcf_16) (desc->buf_addr[HFS_STAT / 2]);

			/* Make sure the frame isn't bad */
			if ((hfs_stat & HFS_STAT_ERR) != HCF_SUCCESS) {
				DBG_WARNING(DbgInfo,
					    "HFS_STAT_ERROR (0x%x) in Rx Packet\n",
					    desc->buf_addr[HFS_STAT / 2]);

				/* Give the descriptor back to the HCF */
				hcf_dma_rx_put(&(lp->hcfCtx), desc);
				return -EIO;
			}

			/* Determine what port this packet is for */
			port = (hfs_stat >> 8) & 0x0007;
			DBG_RX(DbgInfo, "Rx frame for port %d\n", port);

			pktlen = GET_BUF_CNT(desc_next);
			if (pktlen != 0) {
				skb = ALLOC_SKB(pktlen);
				if (skb != NULL) {
					switch (port) {
#ifdef USE_WDS
					case 1:
					case 2:
					case 3:
					case 4:
					case 5:
					case 6:
						skb->dev =
						    lp->wds_port[port - 1].dev;
						break;
#endif /* USE_WDS */

					case 0:
					default:
						skb->dev = dev;
						break;
					}

					GET_PACKET_DMA(skb->dev, skb, pktlen);

					/* Give the descriptor back to the HCF */
					hcf_dma_rx_put(&(lp->hcfCtx), desc);

					netif_rx(skb);

					if (port == 0) {
						lp->stats.rx_packets++;
						lp->stats.rx_bytes += pktlen;
					}
#ifdef USE_WDS
					else {
						lp->wds_port[port -
							     1].stats.
						    rx_packets++;
						lp->wds_port[port -
							     1].stats.
						    rx_bytes += pktlen;
					}
#endif /* USE_WDS */

					dev->last_rx = jiffies;

				} else {
					DBG_ERROR(DbgInfo,
						  "Could not alloc skb\n");

					if (port == 0)
						lp->stats.rx_dropped++;
#ifdef USE_WDS
					else {
						lp->wds_port[port -
							     1].stats.
						    rx_dropped++;
					}
#endif /* USE_WDS */
				}
			}
		}
		/*}*/
	}

	return 0;
}				/* wl_rx_dma */

/*============================================================================*/
#endif /* ENABLE_DMA */