2009-09-27 03:12:52 -06:00
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
2011-12-29 15:09:01 -07:00
|
|
|
|
* Copyright © 2003 Agere Systems Inc.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
* 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
|
|
|
|
|
*
|
2011-12-29 15:09:01 -07:00
|
|
|
|
* THIS SOFTWARE IS PROVIDED AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
2009-09-27 03:12:52 -06:00
|
|
|
|
* 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 cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 02:04:11 -06:00
|
|
|
|
#include <linux/slab.h>
|
2009-09-27 03:12:52 -06:00
|
|
|
|
#include <linux/types.h>
|
|
|
|
|
#include <linux/kernel.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 <linux/skbuff.h>
|
|
|
|
|
// #include <asm/io.h>
|
2012-03-28 11:30:03 -06:00
|
|
|
|
// // #include <asm/bitops.h>
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
|
// #include <linux/skbuff.h>
|
|
|
|
|
// #include <linux/if_arp.h>
|
|
|
|
|
// #include <linux/ioport.h>
|
|
|
|
|
|
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
|
|
#include <hcf.h>
|
|
|
|
|
#include <dhf.h>
|
|
|
|
|
// #include <hcfdef.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 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* global variables
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
#if DBG
|
|
|
|
|
extern dbg_info_t *DbgInfo;
|
|
|
|
|
#endif /* DBG */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if HCF_ENCAP
|
|
|
|
|
#define MTU_MAX (HCF_MAX_MSG - ETH_HLEN - 8)
|
|
|
|
|
#else
|
|
|
|
|
#define MTU_MAX (HCF_MAX_MSG - ETH_HLEN)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//static int mtu = MTU_MAX;
|
|
|
|
|
//MODULE_PARM(mtu, "i");
|
|
|
|
|
//MODULE_PARM_DESC(mtu, "MTU");
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* macros
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
#define BLOCK_INPUT(buf, len) \
|
|
|
|
|
desc->buf_addr = buf; \
|
|
|
|
|
desc->BUF_SIZE = len; \
|
|
|
|
|
status = hcf_rcv_msg(&(lp->hcfCtx), desc, 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 )
|
|
|
|
|
{
|
|
|
|
|
// unsigned long flags;
|
|
|
|
|
// struct wl_private *lp = wl_priv(dev);
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_init" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
DBG_PARAM( DbgInfo, "dev", "%s (0x%p)", dev->name, dev );
|
|
|
|
|
|
|
|
|
|
/* Nothing to do, but grab the spinlock anyway just in case we ever need
|
|
|
|
|
this routine */
|
|
|
|
|
// wl_lock( lp, &flags );
|
|
|
|
|
// wl_unlock( lp, &flags );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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_FUNC( "wl_config" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
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. */
|
2011-10-01 02:43:48 -06:00
|
|
|
|
DBG_TRACE(DbgInfo, "%s: %s called.\n", dev->name, __func__);
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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_FUNC( "wl_stats" );
|
|
|
|
|
//DBG_ENTER( DbgInfo );
|
|
|
|
|
//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 );
|
|
|
|
|
|
|
|
|
|
//DBG_LEAVE( DbgInfo );
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
//DBG_LEAVE( DbgInfo );
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_open" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
if( status != HCF_SUCCESS ) {
|
2009-12-18 13:43:50 -07:00
|
|
|
|
// Unsuccessful, try reset of the card to recover
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
lp->is_handling_int = WL_HANDLING_INT; // Start handling interrupts
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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_FUNC("wl_close");
|
|
|
|
|
DBG_ENTER(DbgInfo);
|
|
|
|
|
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 );
|
|
|
|
|
lp->is_handling_int = WL_NOT_HANDLING_INT; // Stop handling interrupts
|
|
|
|
|
|
|
|
|
|
#ifdef USE_RTS
|
|
|
|
|
if( lp->useRTS == 1 ) {
|
|
|
|
|
DBG_TRACE( DbgInfo, "Skipping device close, in RTS mode\n" );
|
|
|
|
|
wl_unlock( lp, &flags );
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
#endif /* USE_RTS */
|
|
|
|
|
|
|
|
|
|
/* Disable the ports */
|
|
|
|
|
wl_disable( lp );
|
|
|
|
|
|
|
|
|
|
wl_unlock( lp, &flags );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
return 0;
|
|
|
|
|
} // wl_close
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
|
{
|
2013-01-05 17:44:26 -07:00
|
|
|
|
strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
|
|
|
|
|
strlcpy(info->version, DRV_VERSION_STR, sizeof(info->version));
|
|
|
|
|
// strlcpy(info.fw_version, priv->fw_name,
|
|
|
|
|
// sizeof(info.fw_version));
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
if (dev->dev.parent) {
|
|
|
|
|
dev_set_name(dev->dev.parent, "%s", info->bus_info);
|
2013-01-05 17:44:26 -07:00
|
|
|
|
//strlcpy(info->bus_info, dev->dev.parent->bus_id,
|
|
|
|
|
// sizeof(info->bus_info));
|
2009-09-27 03:12:52 -06:00
|
|
|
|
} else {
|
2013-01-05 17:44:26 -07:00
|
|
|
|
snprintf(info->bus_info, sizeof(info->bus_info),
|
2009-09-27 03:12:52 -06:00
|
|
|
|
"PCMCIA FIXME");
|
|
|
|
|
// "PCMCIA 0x%lx", priv->hw.iobase);
|
|
|
|
|
}
|
|
|
|
|
} // 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_FUNC( "wl_ioctl" );
|
|
|
|
|
DBG_ENTER(DbgInfo);
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
return ret;
|
|
|
|
|
} // wl_ioctl
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
|
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, ®s);
|
|
|
|
|
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_FUNC( "wl_tx_timeout" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
} // 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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_send" );
|
|
|
|
|
|
|
|
|
|
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_kfree_skb( 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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_tx" );
|
|
|
|
|
|
|
|
|
|
/* 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 */
|
2012-09-05 22:38:59 -06:00
|
|
|
|
list_move(&(txF->node), &(lp->txQ[0]));
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
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_FUNC("wl_rx")
|
|
|
|
|
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 );
|
|
|
|
|
|
2010-03-24 23:17:07 -06:00
|
|
|
|
pktlen = lp->hcfCtx.IFB_RxLen;
|
|
|
|
|
if (pktlen != 0) {
|
|
|
|
|
skb = ALLOC_SKB(pktlen);
|
|
|
|
|
if (skb != NULL) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
/* 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;
|
2010-04-01 15:22:57 -06:00
|
|
|
|
struct netdev_hw_addr *ha;
|
2009-09-27 03:12:52 -06:00
|
|
|
|
struct wl_private *lp = wl_priv(dev);
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_multicast" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
DBG_PARAM( DbgInfo, "dev", "%s (0x%p)", dev->name, dev );
|
|
|
|
|
|
|
|
|
|
if( !wl_adapter_is_open( dev )) {
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
|
if( DBG_FLAGS( DbgInfo ) & DBG_PARAM_ON ) {
|
|
|
|
|
DBG_PRINT(" flags: %s%s%s\n",
|
2012-04-03 11:38:44 -06:00
|
|
|
|
( dev->flags & IFF_PROMISC ) ? "Promiscuous " : "",
|
2009-09-27 03:12:52 -06:00
|
|
|
|
( dev->flags & IFF_MULTICAST ) ? "Multicast " : "",
|
|
|
|
|
( dev->flags & IFF_ALLMULTI ) ? "All-Multicast" : "" );
|
|
|
|
|
|
2010-02-07 21:30:35 -07:00
|
|
|
|
DBG_PRINT( " mc_count: %d\n", netdev_mc_count(dev));
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
2010-04-01 15:22:57 -06:00
|
|
|
|
netdev_for_each_mc_addr(ha, dev)
|
2010-10-16 04:11:12 -06:00
|
|
|
|
DBG_PRINT(" %pM (%d)\n", ha->addr, dev->addr_len);
|
2009-09-27 03:12:52 -06:00
|
|
|
|
}
|
|
|
|
|
#endif /* DBG */
|
|
|
|
|
|
|
|
|
|
if(!( lp->flags & WVLAN2_UIL_BUSY )) {
|
|
|
|
|
|
|
|
|
|
#ifdef USE_RTS
|
|
|
|
|
if( lp->useRTS == 1 ) {
|
|
|
|
|
DBG_TRACE( DbgInfo, "Skipping multicast, in RTS mode\n" );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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 ));
|
|
|
|
|
}
|
2010-02-07 21:30:35 -07:00
|
|
|
|
else if ((netdev_mc_count(dev) > HCF_MAX_MULTICAST) ||
|
2009-09-27 03:12:52 -06:00
|
|
|
|
( 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 );
|
|
|
|
|
}
|
2010-02-07 21:30:35 -07:00
|
|
|
|
else if (!netdev_mc_empty(dev)) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
/* Set the multicast addresses */
|
2010-02-07 21:30:35 -07:00
|
|
|
|
lp->ltvRecord.len = ( netdev_mc_count(dev) * 3 ) + 1;
|
2009-09-27 03:12:52 -06:00
|
|
|
|
lp->ltvRecord.typ = CFG_GROUP_ADDR;
|
|
|
|
|
|
2010-02-17 22:10:14 -07:00
|
|
|
|
x = 0;
|
2010-04-01 15:22:57 -06:00
|
|
|
|
netdev_for_each_mc_addr(ha, dev)
|
2010-02-17 22:10:14 -07:00
|
|
|
|
memcpy(&(lp->ltvRecord.u.u8[x++ * ETH_ALEN]),
|
2010-04-01 15:22:57 -06:00
|
|
|
|
ha->addr, ETH_ALEN);
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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 );
|
|
|
|
|
}
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
#endif /* HCF_STA */
|
|
|
|
|
} // wl_multicast
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
#else /* NEW_MULTICAST */
|
|
|
|
|
|
|
|
|
|
void wl_multicast( struct net_device *dev, int num_addrs, void *addrs )
|
|
|
|
|
{
|
|
|
|
|
DBG_FUNC( "wl_multicast");
|
|
|
|
|
DBG_ENTER(DbgInfo);
|
|
|
|
|
|
|
|
|
|
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!
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
} // 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,
|
2011-08-16 00:29:01 -06:00
|
|
|
|
.ndo_set_rx_mode = &wl_multicast,
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_device_alloc" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
/* 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 );
|
|
|
|
|
|
2012-08-27 08:21:13 -06:00
|
|
|
|
/* Allocate virtual devices for WDS support if needed */
|
2009-09-27 03:12:52 -06:00
|
|
|
|
WL_WDS_DEVICE_ALLOC( lp );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
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 )
|
|
|
|
|
{
|
|
|
|
|
// struct wl_private *lp = wl_priv(dev);
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_device_dealloc" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
/* Dealloc the WDS ports */
|
|
|
|
|
WL_WDS_DEVICE_DEALLOC( lp );
|
|
|
|
|
|
|
|
|
|
free_netdev( dev );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
} // 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_port0
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_wds_device_alloc" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2012-03-23 09:44:02 -06:00
|
|
|
|
dev_wds = kzalloc(sizeof(struct net_device), GFP_KERNEL);
|
|
|
|
|
if (!dev_wds) {
|
|
|
|
|
DBG_LEAVE(DbgInfo);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
} // 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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_wds_device_dealloc" );
|
|
|
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-28 11:08:11 -06:00
|
|
|
|
free_netdev(dev_wds);
|
2009-09-27 03:12:52 -06:00
|
|
|
|
lp->wds_port[count].dev = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
|
} // wl_wds_device_dealloc
|
|
|
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* wl_wds_netif_start_queue()
|
|
|
|
|
*******************************************************************************
|
|
|
|
|
*
|
|
|
|
|
* DESCRIPTION:
|
|
|
|
|
*
|
|
|
|
|
* Used to start the netif queues of all the "virtual" network devices
|
2012-09-10 17:01:27 -06:00
|
|
|
|
* which represent the WDS ports.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2012-09-10 17:01:27 -06:00
|
|
|
|
* which represent the WDS ports.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2012-09-10 17:01:27 -06:00
|
|
|
|
* which represent the WDS ports.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2012-09-10 17:01:27 -06:00
|
|
|
|
* "virtual" network devices which represent the WDS ports.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2012-09-10 17:01:27 -06:00
|
|
|
|
* the "virtual" network devices which represent the WDS ports.
|
2009-09-27 03:12:52 -06:00
|
|
|
|
*
|
|
|
|
|
* PARAMETERS:
|
|
|
|
|
*
|
|
|
|
|
* lp - a pointer to the device's private adapter structure
|
|
|
|
|
*
|
|
|
|
|
* RETURNS:
|
|
|
|
|
*
|
|
|
|
|
* N/A
|
|
|
|
|
*
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
void wl_wds_netif_carrier_off( struct wl_private *lp )
|
|
|
|
|
{
|
2012-08-18 12:54:09 -06:00
|
|
|
|
int count;
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
2012-08-18 12:54:10 -06:00
|
|
|
|
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);
|
2012-08-18 12:54:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
} // 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;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC( "wl_send_dma" );
|
|
|
|
|
|
2012-08-18 12:54:12 -06:00
|
|
|
|
if( lp == NULL ) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
DBG_ERROR( DbgInfo, "Private adapter struct is NULL\n" );
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-18 12:54:12 -06:00
|
|
|
|
if( lp->dev == NULL ) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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 */
|
|
|
|
|
|
2012-08-18 12:54:12 -06:00
|
|
|
|
if( skb == NULL ) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
DBG_WARNING (DbgInfo, "Nothing to send.\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = skb->len;
|
|
|
|
|
|
|
|
|
|
/* Get a free descriptor */
|
|
|
|
|
desc = wl_pci_dma_get_tx_packet( lp );
|
|
|
|
|
|
2012-08-18 12:54:12 -06:00
|
|
|
|
if( desc == NULL ) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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( 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;
|
|
|
|
|
|
2012-08-18 12:54:12 -06:00
|
|
|
|
if( desc_next->buf_addr == NULL ) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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_kfree_skb( 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;
|
|
|
|
|
//CFG_MB_INFO_RANGE2_STRCT x;
|
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DBG_FUNC("wl_rx")
|
|
|
|
|
DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);
|
|
|
|
|
|
2010-06-29 04:16:25 -06:00
|
|
|
|
if((( lp = dev->priv ) != NULL ) &&
|
|
|
|
|
!( lp->flags & WVLAN2_UIL_BUSY )) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
|
|
|
|
|
#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 );
|
|
|
|
|
|
2010-03-24 23:17:07 -06:00
|
|
|
|
pktlen = GET_BUF_CNT(desc_next);
|
|
|
|
|
if (pktlen != 0) {
|
|
|
|
|
skb = ALLOC_SKB(pktlen);
|
|
|
|
|
if (skb != NULL) {
|
2009-09-27 03:12:52 -06:00
|
|
|
|
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
|