aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/wlags49_h2/wl_netdev.c
diff options
context:
space:
mode:
authorHenk de Groot <henk.de.groot@hetnet.nl>2009-09-27 11:12:52 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 12:23:10 -0800
commit68c0bdff7ac903421f224e080499c51cd5287f97 (patch)
tree5f5f46e0a2d077c4d039a67d128a15a388dac91f /drivers/staging/wlags49_h2/wl_netdev.c
parentStaging: rtl8187su: remove unused #include <linux/version.h> (diff)
downloadlinux-dev-68c0bdff7ac903421f224e080499c51cd5287f97.tar.xz
linux-dev-68c0bdff7ac903421f224e080499c51cd5287f97.zip
Staging: wlags49_h2: add Agere driver for HERMES II and HERMES II.5 chipsets
WLAN driver for cards using the HERMES II and HERMES II.5 chipset Based on Agere Systems Linux LKM Wireless Driver Source Code, Version 7.22; complies with Open Source BSD License. The software is a modified version of wl_lkm_722_abg.tar.gz from the Agere Systems website, addapted for Ubuntu 9.04 and modified to fit in the current Linux kernel (2.6.31). Modified for kernel 2.6 by Henk de Groot <pe1dnn@amsat.org> Based on 7.18 version by Andrey Borzenkov <arvidjaar@mail.ru> $Revision: 39 $ Signed-off-by: Henk de Groot <pe1dnn@amsat.org> Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/wlags49_h2/wl_netdev.c')
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c2085
1 files changed, 2085 insertions, 0 deletions
diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c
new file mode 100644
index 000000000000..a00207b17d2b
--- /dev/null
+++ b/drivers/staging/wlags49_h2/wl_netdev.c
@@ -0,0 +1,2085 @@
+/*******************************************************************************
+ * 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.
+ *
+ ******************************************************************************/
+
+
+
+/*******************************************************************************
+ * VERSION CONTROL INFORMATION
+ *******************************************************************************
+ *
+ * $Author: nico $
+ * $Date: 2004/08/03 13:33:44 $
+ * $Revision: 1.8 $
+ * $Source: /usr/local/cvs/wl_lkm/wireless/wl_netdev.c,v $
+ *
+ ******************************************************************************/
+
+
+
+/*******************************************************************************
+ * include files
+ ******************************************************************************/
+#include <wl_version.h>
+
+#include <linux/module.h>
+#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>
+// #include <asm/system.h>
+// #include <asm/bitops.h>
+
+#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. */
+ DBG_TRACE( DbgInfo, "%s: %s called.\n", dev->name, __FUNC__ );
+
+ 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 ) {
+ // Unsuccesfull, try reset of the card to recover
+ 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)
+{
+ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
+ strncpy(info->version, DRV_VERSION_STR, sizeof(info->version) - 1);
+// strncpy(info.fw_version, priv->fw_name,
+// sizeof(info.fw_version) - 1);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,20))
+ if (dev->dev.parent) {
+ dev_set_name(dev->dev.parent, "%s", info->bus_info);
+ //strncpy(info->bus_info, dev->dev.parent->bus_id,
+ // sizeof(info->bus_info) - 1);
+#else
+ if (dev->class_dev.parent) {
+ sizeof(info->bus_info) - 1);
+#endif
+ } else {
+ snprintf(info->bus_info, sizeof(info->bus_info) - 1,
+ "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, &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_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 );
+ return;
+} // 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 */
+ list_del( &( txF->node ));
+ list_add( &( 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_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 );
+
+ if(( pktlen = lp->hcfCtx.IFB_RxLen ) != 0 ) {
+ if(( skb = ALLOC_SKB( pktlen )) != 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 dev_mc_list *mclist;
+ 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",
+ ( dev->flags & IFF_PROMISC ) ? "Promiscous " : "",
+ ( dev->flags & IFF_MULTICAST ) ? "Multicast " : "",
+ ( dev->flags & IFF_ALLMULTI ) ? "All-Multicast" : "" );
+
+ DBG_PRINT( " mc_count: %d\n", dev->mc_count );
+
+ for( x = 0, mclist = dev->mc_list; mclist && x < dev->mc_count;
+ x++, mclist = mclist->next ) {
+ DBG_PRINT( " %s (%d)\n", DbgHwAddr(mclist->dmi_addr),
+ mclist->dmi_addrlen );
+ }
+ }
+#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 ));
+ }
+ else if(( dev->mc_count > 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( dev->mc_count != 0 ) {
+ /* Set the multicast addresses */
+ lp->ltvRecord.len = ( dev->mc_count * 3 ) + 1;
+ lp->ltvRecord.typ = CFG_GROUP_ADDR;
+
+ for( x = 0, mclist = dev->mc_list;
+ ( x < dev->mc_count ) && ( mclist != NULL );
+ x++, mclist = mclist->next ) {
+ memcpy( &( lp->ltvRecord.u.u8[x * ETH_ALEN] ),
+ mclist->dmi_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 );
+ }
+ 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 */
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+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_multicast_list = &wl_multicast,
+
+ .ndo_init = &wl_insert,
+ .ndo_open = &wl_adapter_open,
+ .ndo_stop = &wl_adapter_close,
+ .ndo_do_ioctl = &wl_ioctl,
+
+#ifdef HAVE_TX_TIMEOUT
+ .ndo_tx_timeout = &wl_tx_timeout,
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = wl_poll,
+#endif
+};
+#endif // (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+
+/*******************************************************************************
+ * 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;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ dev->netdev_ops = &wl_netdev_ops;
+#else
+ dev->hard_start_xmit = &wl_tx_port0;
+
+ dev->set_config = &wl_config;
+ dev->get_stats = &wl_stats;
+ dev->set_multicast_list = &wl_multicast;
+
+ dev->init = &wl_insert;
+ dev->open = &wl_adapter_open;
+ dev->stop = &wl_adapter_close;
+ dev->do_ioctl = &wl_ioctl;
+
+#ifdef HAVE_TX_TIMEOUT
+ dev->tx_timeout = &wl_tx_timeout;
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = wl_poll;
+#endif
+
+#endif // (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+
+#ifdef HAVE_TX_TIMEOUT
+ dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+ dev->ethtool_ops = &wl_ethtool_ops;
+
+ netif_stop_queue( dev );
+
+ /* Allocate virutal devices for WDS support if needed */
+ 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 );
+ return;
+} // 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;
+
+ dev_wds = kmalloc( sizeof( struct net_device ), GFP_KERNEL );
+ memset( dev_wds, 0, sizeof( struct net_device ));
+
+ 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 );
+ return;
+} // 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 );
+ }
+
+ kfree( dev_wds );
+ lp->wds_port[count].dev = NULL;
+ }
+ }
+
+ DBG_LEAVE( DbgInfo );
+ return;
+} // wl_wds_device_dealloc
+/*============================================================================*/
+
+/*******************************************************************************
+ * wl_wds_netif_start_queue()
+ *******************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * Used to start the netif queues of all the "virtual" network devices
+ * which repesent 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;
+ }
+ }
+ }
+
+ return;
+} // wl_wds_netif_start_queue
+/*============================================================================*/
+
+/*******************************************************************************
+ * wl_wds_netif_stop_queue()
+ *******************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * Used to stop the netif queues of all the "virtual" network devices
+ * which repesent 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;
+ }
+ }
+ }
+
+ return;
+} // wl_wds_netif_stop_queue
+/*============================================================================*/
+
+/*******************************************************************************
+ * wl_wds_netif_wake_queue()
+ *******************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * Used to wake the netif queues of all the "virtual" network devices
+ * which repesent 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;
+ }
+ }
+ }
+
+ return;
+} // 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 repesent 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 );
+ }
+ }
+ }
+
+ return;
+} // 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 repesent 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 );
+ }
+ }
+ }
+
+ return;
+} // 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" );
+
+ 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( 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_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);
+
+ if((( lp = (struct wl_private *)dev->priv ) != 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 );
+
+ if(( pktlen = GET_BUF_CNT( desc_next )) != 0 ) {
+ if(( skb = ALLOC_SKB( pktlen )) != 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