| /******************************************************************************* |
| * Agere Systems Inc. |
| * Wireless device driver for Linux (wlags49). |
| * |
| * Copyright (c) 1998-2003 Agere Systems Inc. |
| * All rights reserved. |
| * http://www.agere.com |
| * |
| * Initially developed by TriplePoint, Inc. |
| * http://www.triplepoint.com |
| * |
| *------------------------------------------------------------------------------ |
| * |
| * This file contains handler functions registered with the net_device |
| * structure. |
| * |
| *------------------------------------------------------------------------------ |
| * |
| * SOFTWARE LICENSE |
| * |
| * This software is provided subject to the following terms and conditions, |
| * which you should read carefully before using the software. Using this |
| * software indicates your acceptance of these terms and conditions. If you do |
| * not agree with these terms and conditions, do not use the software. |
| * |
| * Copyright © 2003 Agere Systems Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source or binary forms, with or without |
| * modifications, are permitted provided that the following conditions are met: |
| * |
| * . Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following Disclaimer as comments in the code as |
| * well as in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * . Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following Disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * . Neither the name of Agere Systems Inc. nor the names of the contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * Disclaimer |
| * |
| * THIS SOFTWARE IS PROVIDED AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, |
| * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ANY |
| * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN |
| * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| ******************************************************************************/ |
| |
| /******************************************************************************* |
| * include files |
| ******************************************************************************/ |
| #include <wl_version.h> |
| |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| // #include <linux/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 ) { |
| // Unsuccessful, 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 (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 { |
| 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, ®s); |
| wl_unlock( lp, &flags ); |
| } |
| #endif |
| |
| /******************************************************************************* |
| * wl_tx_timeout() |
| ******************************************************************************* |
| * |
| * DESCRIPTION: |
| * |
| * The handler called when, for some reason, a Tx request is not completed. |
| * |
| * PARAMETERS: |
| * |
| * dev - a pointer to the device's net_device struct. |
| * |
| * RETURNS: |
| * |
| * N/A |
| * |
| ******************************************************************************/ |
| void wl_tx_timeout( struct net_device *dev ) |
| { |
| #ifdef USE_WDS |
| int count; |
| #endif /* USE_WDS */ |
| unsigned long flags; |
| struct wl_private *lp = wl_priv(dev); |
| struct net_device_stats *pStats = NULL; |
| /*------------------------------------------------------------------------*/ |
| |
| DBG_FUNC( "wl_tx_timeout" ); |
| DBG_ENTER( DbgInfo ); |
| |
| DBG_WARNING( DbgInfo, "%s: Transmit timeout.\n", dev->name ); |
| |
| wl_lock( lp, &flags ); |
| |
| #ifdef USE_RTS |
| if( lp->useRTS == 1 ) { |
| DBG_TRACE( DbgInfo, "Skipping tx_timeout handler, in RTS mode\n" ); |
| wl_unlock( lp, &flags ); |
| |
| DBG_LEAVE( DbgInfo ); |
| return; |
| } |
| #endif /* USE_RTS */ |
| |
| /* Figure out which device (the "root" device or WDS port) this timeout |
| is for */ |
| #ifdef USE_WDS |
| |
| for( count = 0; count < NUM_WDS_PORTS; count++ ) { |
| if( dev == lp->wds_port[count].dev ) { |
| pStats = &( lp->wds_port[count].stats ); |
| |
| /* Break the loop so that we can use the counter to access WDS |
| information in the private structure */ |
| break; |
| } |
| } |
| |
| #endif /* USE_WDS */ |
| |
| /* If pStats is still NULL, then the device is not a WDS port */ |
| if( pStats == NULL ) { |
| pStats = &( lp->stats ); |
| } |
| |
| /* Accumulate the timeout error */ |
| pStats->tx_errors++; |
| |
| wl_unlock( lp, &flags ); |
| |
| DBG_LEAVE( DbgInfo ); |
| 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 ); |
| |
| pktlen = lp->hcfCtx.IFB_RxLen; |
| if (pktlen != 0) { |
| skb = ALLOC_SKB(pktlen); |
| if (skb != NULL) { |
| /* Set the netdev based on the port */ |
| switch( port ) { |
| #ifdef USE_WDS |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| skb->dev = lp->wds_port[port-1].dev; |
| break; |
| #endif /* USE_WDS */ |
| |
| case 0: |
| default: |
| skb->dev = dev; |
| break; |
| } |
| |
| desc = &( lp->desc_rx ); |
| |
| desc->next_desc_addr = NULL; |
| |
| /* |
| #define BLOCK_INPUT(buf, len) \ |
| desc->buf_addr = buf; \ |
| desc->BUF_SIZE = len; \ |
| status = hcf_rcv_msg(&(lp->hcfCtx), desc, 0) |
| */ |
| |
| GET_PACKET( skb->dev, skb, pktlen ); |
| |
| if( status == HCF_SUCCESS ) { |
| netif_rx( skb ); |
| |
| if( port == 0 ) { |
| lp->stats.rx_packets++; |
| lp->stats.rx_bytes += pktlen; |
| } |
| #ifdef USE_WDS |
| else |
| { |
| lp->wds_port[port-1].stats.rx_packets++; |
| lp->wds_port[port-1].stats.rx_bytes += pktlen; |
| } |
| #endif /* USE_WDS */ |
| |
| dev->last_rx = jiffies; |
| |
| #ifdef WIRELESS_EXT |
| #ifdef WIRELESS_SPY |
| if( lp->spydata.spy_number > 0 ) { |
| char *srcaddr = skb->mac.raw + MAC_ADDR_SIZE; |
| |
| wl_spy_gather( dev, srcaddr ); |
| } |
| #endif /* WIRELESS_SPY */ |
| #endif /* WIRELESS_EXT */ |
| } else { |
| DBG_ERROR( DbgInfo, "Rx request to card FAILED\n" ); |
| |
| if( port == 0 ) { |
| lp->stats.rx_dropped++; |
| } |
| #ifdef USE_WDS |
| else |
| { |
| lp->wds_port[port-1].stats.rx_dropped++; |
| } |
| #endif /* USE_WDS */ |
| |
| dev_kfree_skb( skb ); |
| } |
| } else { |
| DBG_ERROR( DbgInfo, "Could not alloc skb\n" ); |
| |
| if( port == 0 ) { |
| lp->stats.rx_dropped++; |
| } |
| #ifdef USE_WDS |
| else |
| { |
| lp->wds_port[port-1].stats.rx_dropped++; |
| } |
| #endif /* USE_WDS */ |
| } |
| } |
| } |
| |
| return 0; |
| } // wl_rx |
| /*============================================================================*/ |
| |
| /******************************************************************************* |
| * wl_multicast() |
| ******************************************************************************* |
| * |
| * DESCRIPTION: |
| * |
| * Function to handle multicast packets |
| * |
| * PARAMETERS: |
| * |
| * dev - a pointer to the device's net_device structure. |
| * |
| * RETURNS: |
| * |
| * N/A |
| * |
| ******************************************************************************/ |
| #ifdef NEW_MULTICAST |
| |
| void wl_multicast( struct net_device *dev ) |
| { |
| #if 1 //;? (HCF_TYPE) & HCF_TYPE_STA //;?should we return an error status in AP mode |
| //;?seems reasonable that even an AP-only driver could afford this small additional footprint |
| |
| int x; |
| struct netdev_hw_addr *ha; |
| struct wl_private *lp = wl_priv(dev); |
| unsigned long flags; |
| /*------------------------------------------------------------------------*/ |
| |
| DBG_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", netdev_mc_count(dev)); |
| |
| netdev_for_each_mc_addr(ha, dev) |
| DBG_PRINT(" %s (%d)\n", DbgHwAddr(ha->addr), |
| dev->addr_len); |
| } |
| #endif /* DBG */ |
| |
| if(!( lp->flags & WVLAN2_UIL_BUSY )) { |
| |
| #ifdef USE_RTS |
| if( lp->useRTS == 1 ) { |
| DBG_TRACE( DbgInfo, "Skipping multicast, in RTS mode\n" ); |
| |
| 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 ((netdev_mc_count(dev) > HCF_MAX_MULTICAST) || |
| ( dev->flags & IFF_ALLMULTI )) { |
| /* Shutting off this filter will enable all multicast frames to |
| be sent up from the device; however, this is a static RID, so |
| a call to wl_apply() is needed */ |
| lp->ltvRecord.len = 2; |
| lp->ltvRecord.typ = CFG_CNF_RX_ALL_GROUP_ADDR; |
| lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( 0 ); |
| DBG_PRINT( "Enabling all multicast mode (IFF_ALLMULTI)\n" ); |
| hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); |
| wl_apply( lp ); |
| } |
| else if (!netdev_mc_empty(dev)) { |
| /* Set the multicast addresses */ |
| lp->ltvRecord.len = ( netdev_mc_count(dev) * 3 ) + 1; |
| lp->ltvRecord.typ = CFG_GROUP_ADDR; |
| |
| x = 0; |
| netdev_for_each_mc_addr(ha, dev) |
| memcpy(&(lp->ltvRecord.u.u8[x++ * ETH_ALEN]), |
| ha->addr, ETH_ALEN); |
| DBG_PRINT( "Setting multicast list\n" ); |
| hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); |
| } else { |
| /* Disable promiscuous mode */ |
| lp->ltvRecord.len = 2; |
| lp->ltvRecord.typ = CFG_PROMISCUOUS_MODE; |
| lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( 0 ); |
| DBG_PRINT( "Disabling Promiscuous mode\n" ); |
| hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); |
| |
| /* Disable multicast mode */ |
| lp->ltvRecord.len = 2; |
| lp->ltvRecord.typ = CFG_GROUP_ADDR; |
| DBG_PRINT( "Disabling Multicast mode\n" ); |
| hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); |
| |
| /* Turning on this filter will prevent all multicast frames from |
| being sent up from the device; however, this is a static RID, |
| so a call to wl_apply() is needed */ |
| lp->ltvRecord.len = 2; |
| lp->ltvRecord.typ = CFG_CNF_RX_ALL_GROUP_ADDR; |
| lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( 1 ); |
| DBG_PRINT( "Disabling all multicast mode (IFF_ALLMULTI)\n" ); |
| hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); |
| wl_apply( lp ); |
| } |
| } |
| wl_act_int_on( lp ); |
| wl_unlock( lp, &flags ); |
| } |
| DBG_LEAVE( DbgInfo ); |
| #endif /* HCF_STA */ |
| } // wl_multicast |
| /*============================================================================*/ |
| |
| #else /* NEW_MULTICAST */ |
| |
| void wl_multicast( struct net_device *dev, int num_addrs, void *addrs ) |
| { |
| DBG_FUNC( "wl_multicast"); |
| DBG_ENTER(DbgInfo); |
| |
| DBG_PARAM( DbgInfo, "dev", "%s (0x%p)", dev->name, dev ); |
| DBG_PARAM( DbgInfo, "num_addrs", "%d", num_addrs ); |
| DBG_PARAM( DbgInfo, "addrs", "0x%p", addrs ); |
| |
| #error Obsolete set multicast interface! |
| |
| DBG_LEAVE( DbgInfo ); |
| } // wl_multicast |
| /*============================================================================*/ |
| |
| #endif /* NEW_MULTICAST */ |
| |
| static const struct net_device_ops wl_netdev_ops = |
| { |
| .ndo_start_xmit = &wl_tx_port0, |
| |
| .ndo_set_config = &wl_config, |
| .ndo_get_stats = &wl_stats, |
| .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, |
| |
| .ndo_tx_timeout = &wl_tx_timeout, |
| |
| #ifdef CONFIG_NET_POLL_CONTROLLER |
| .ndo_poll_controller = wl_poll, |
| #endif |
| }; |
| |
| /******************************************************************************* |
| * wl_device_alloc() |
| ******************************************************************************* |
| * |
| * DESCRIPTION: |
| * |
| * Create instances of net_device and wl_private for the new adapter |
| * and register the device's entry points in the net_device structure. |
| * |
| * PARAMETERS: |
| * |
| * N/A |
| * |
| * RETURNS: |
| * |
| * a pointer to an allocated and initialized net_device struct for this |
| * device. |
| * |
| ******************************************************************************/ |
| struct net_device * wl_device_alloc( void ) |
| { |
| struct net_device *dev = NULL; |
| struct wl_private *lp = NULL; |
| /*------------------------------------------------------------------------*/ |
| |
| DBG_FUNC( "wl_device_alloc" ); |
| DBG_ENTER( DbgInfo ); |
| |
| /* Alloc a net_device struct */ |
| dev = alloc_etherdev(sizeof(struct wl_private)); |
| if (!dev) |
| return NULL; |
| |
| /* Initialize the 'next' pointer in the struct. Currently only used for PCI, |
| but do it here just in case it's used for other buses in the future */ |
| lp = wl_priv(dev); |
| |
| |
| /* Check MTU */ |
| if( dev->mtu > MTU_MAX ) |
| { |
| DBG_WARNING( DbgInfo, "%s: MTU set too high, limiting to %d.\n", |
| dev->name, MTU_MAX ); |
| dev->mtu = MTU_MAX; |
| } |
| |
| /* Setup the function table in the device structure. */ |
| |
| dev->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; |
| lp->wireless_data.spy_data = &lp->spy_data; |
| dev->wireless_data = &lp->wireless_data; |
| |
| dev->netdev_ops = &wl_netdev_ops; |
| |
| dev->watchdog_timeo = TX_TIMEOUT; |
| |
| dev->ethtool_ops = &wl_ethtool_ops; |
| |
| netif_stop_queue( dev ); |
| |
| /* 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 ); |
| |
| pktlen = GET_BUF_CNT(desc_next); |
| if (pktlen != 0) { |
| skb = ALLOC_SKB(pktlen); |
| if (skb != NULL) { |
| switch( port ) { |
| #ifdef USE_WDS |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| skb->dev = lp->wds_port[port-1].dev; |
| break; |
| #endif /* USE_WDS */ |
| |
| case 0: |
| default: |
| skb->dev = dev; |
| break; |
| } |
| |
| GET_PACKET_DMA( skb->dev, skb, pktlen ); |
| |
| /* Give the descriptor back to the HCF */ |
| hcf_dma_rx_put( &( lp->hcfCtx ), desc ); |
| |
| netif_rx( skb ); |
| |
| if( port == 0 ) { |
| lp->stats.rx_packets++; |
| lp->stats.rx_bytes += pktlen; |
| } |
| #ifdef USE_WDS |
| else |
| { |
| lp->wds_port[port-1].stats.rx_packets++; |
| lp->wds_port[port-1].stats.rx_bytes += pktlen; |
| } |
| #endif /* USE_WDS */ |
| |
| dev->last_rx = jiffies; |
| |
| } else { |
| DBG_ERROR( DbgInfo, "Could not alloc skb\n" ); |
| |
| if( port == 0 ) |
| { |
| lp->stats.rx_dropped++; |
| } |
| #ifdef USE_WDS |
| else |
| { |
| lp->wds_port[port-1].stats.rx_dropped++; |
| } |
| #endif /* USE_WDS */ |
| } |
| } |
| } |
| //} |
| } |
| |
| return 0; |
| } // wl_rx_dma |
| /*============================================================================*/ |
| #endif // ENABLE_DMA |