aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/wlags49_h2/wl_netdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/wlags49_h2/wl_netdev.c')
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c2070
1 files changed, 2070 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..ac3890247965
--- /dev/null
+++ b/drivers/staging/wlags49_h2/wl_netdev.c
@@ -0,0 +1,2070 @@
+/*******************************************************************************
+ * Agere Systems Inc.
+ * Wireless device driver for Linux (wlags49).
+ *
+ * Copyright (c) 1998-2003 Agere Systems Inc.
+ * All rights reserved.
+ * http://www.agere.com
+ *
+ * Initially developed by TriplePoint, Inc.
+ * http://www.triplepoint.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * This file contains handler functions registered with the net_device
+ * structure.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software. Using this
+ * software indicates your acceptance of these terms and conditions. If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2003 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following Disclaimer as comments in the code as
+ * well as in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following Disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ * include files
+ ******************************************************************************/
+#include <wl_version.h>
+
+#include <linux/module.h>
+#include <linux/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