/******************************************************************************
  
  Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
  
  ieee802.11 header construcion, some TX logic and misc modifications
  for rtl8180-sa2400 driver added by Andrea Merello <andreamrl@tiscali.it>
  
  This program is free software; you can redistribute it and/or modify it 
  under the terms of version 2 of the GNU General Public License as 
  published by the Free Software Foundation.
  
  This program is distributed in the hope that it will be useful, but WITHOUT 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59 
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.
  
  Contact Information:
  James P. Ketrenos <ipw2100-admin@linux.intel.com>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  
  Please note that the file has been heavily modified for the rtl8180-sa2400
  driver, so please don't contact the above person for bugs or other issues
  if you are not sure they are related to the original code

******************************************************************************/
#include <linux/compiler.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <asm/uaccess.h>

#include "ieee80211.h"
/*
#define DEBUG_SKB2TXB
#define DEBUG_HARD_XMIT
*/

/*

802.11 frame_contorl for data frames - 2 bytes
     ,-----------------------------------------------------------------------------------------.
bits | 0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |  a  |  b  |  c  |  d  |  e   |
     |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
val  | 0  |  0  |  0  |  1  |  x  |  0  |  0  |  0  |  1  |  0  |  x  |  x  |  x  |  x  |  x   |
     |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
desc | ^-ver-^  |  ^type-^  |  ^-----subtype-----^  | to  |from |more |retry| pwr |more |wep   |
     |          |           | x=0 data,x=1 data+ack | DS  | DS  |frag |     | mgm |data |      |
     '-----------------------------------------------------------------------------------------'
		                                    /\
                                                    |
802.11 Data Frame                                   |
           ,--------- 'ctrl' expands to >-----------'
          |
      ,---'--,
      |      |------------------------------------------------------------.
Bytes |  2   |  2   |    6    |    6    |    6    |  2   | 0..2312 |   4  |
      |------|------|---------|---------|---------|------|---------|------|
Desc. | ctrl | dura |  DA/RA  |   TA    |    SA   | Sequ |  Frame  |  fcs |
      |      | tion | (BSSID) |         |         | ence |  data   |      |
      `--------------------------------------------------|         |------'
Total: 28 non-data bytes                                 `----.----'  
                                                              |
       .- 'Frame data' expands to <---------------------------'
       |
       V
      ,---------------------------------------------------.
Bytes |  1   |  1   |    1    |    3     |  2   |  0-2304 |
      |------|------|---------|----------|------|---------|
Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP      |
      | DSAP | SSAP |         |          |      | Packet  |
      | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8|      |         |
      `-----------------------------------------|         |
Total: 8 non-data bytes                         `----.----'
                                                     |
       .- 'IP Packet' expands, if WEP enabled, to <--'
       |
       V
      ,-----------------------.
Bytes |  4  |   0-2296  |  4  |
      |-----|-----------|-----|
Desc. | IV  | Encrypted | ICV |
      |     | IP Packet |     |
      `-----------------------'
Total: 8 non-data bytes


802.3 Ethernet Data Frame 

      ,-----------------------------------------.
Bytes |   6   |   6   |  2   |  Variable |   4  |
      |-------|-------|------|-----------|------|
Desc. | Dest. | Source| Type | IP Packet |  fcs |
      |  MAC  |  MAC  |      |           |      |
      `-----------------------------------------'
Total: 18 non-data bytes

In the event that fragmentation is required, the incoming payload is split into
N parts of size ieee->fts.  The first fragment contains the SNAP header and the
remaining packets are just data.

If encryption is enabled, each fragment payload size is reduced by enough space
to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
So if you have 1500 bytes of payload with ieee->fts set to 500 without 
encryption it will take 3 frames.  With WEP it will take 4 frames as the 
payload of each frame is reduced to 492 bytes.

* SKB visualization
* 
*  ,- skb->data 
* |
* |    ETHERNET HEADER        ,-<-- PAYLOAD
* |                           |     14 bytes from skb->data 
* |  2 bytes for Type --> ,T. |     (sizeof ethhdr)
* |                       | | |    
* |,-Dest.--. ,--Src.---. | | |     
* |  6 bytes| | 6 bytes | | | |
* v         | |         | | | |
* 0         | v       1 | v | v           2       
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
*     ^     | ^         | ^ | 
*     |     | |         | | |  
*     |     | |         | `T' <---- 2 bytes for Type
*     |     | |         |
*     |     | '---SNAP--' <-------- 6 bytes for SNAP
*     |     |  
*     `-IV--' <-------------------- 4 bytes for IV (WEP)
*
*      SNAP HEADER
*
*/

static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };

static inline int ieee80211_put_snap(u8 *data, u16 h_proto)
{
	struct ieee_802_11_snap_header *snap;
	u8 *oui;

	snap = (struct ieee_802_11_snap_header *)data;
	snap->dsap = 0xaa;
	snap->ssap = 0xaa;
	snap->ctrl = 0x03;

	if (h_proto == 0x8137 || h_proto == 0x80f3)
		oui = P802_1H_OUI;
	else
		oui = RFC1042_OUI;
	snap->oui[0] = oui[0];
	snap->oui[1] = oui[1];
	snap->oui[2] = oui[2];

	*(u16 *)(data + SNAP_SIZE) = htons(h_proto);

	return SNAP_SIZE + sizeof(u16);
}

#ifndef CONFIG_IEEE80211_NOWEP
static inline int ieee80211_encrypt_fragment(
	struct ieee80211_device *ieee, 
	struct sk_buff *frag,
	int hdr_len)
{
	struct ieee80211_crypt_data* crypt = ieee->crypt[ieee->tx_keyidx];
	int res;
	
#ifdef NOT_YET
	if (ieee->tkip_countermeasures &&
	    crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
		hdr = (struct ieee80211_hdr *) skb->data;
		if (net_ratelimit()) {
			printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
			       "TX packet to " MACSTR "\n",
			       ieee->dev->name, MAC2STR(hdr->addr1));
		}
		return NULL;
	}
#endif

	/* To encrypt, frame format is:
	 * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */

	// PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption.
	/* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
	 * call both MSDU and MPDU encryption functions from here. */
	atomic_inc(&crypt->refcnt);
	res = 0;
	if (crypt->ops->encrypt_msdu)
		res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv);
	if (res == 0 && crypt->ops->encrypt_mpdu)
		res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
	
	atomic_dec(&crypt->refcnt);
	if (res < 0) {
		printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
		       ieee->dev->name, frag->len);
		ieee->ieee_stats.tx_discards++;
		return -1;
	}

	return 0;
}
#endif

void ieee80211_r8180_txb_free(struct ieee80211_txb *txb) {
	int i;
	//static int alloc=0;
	if (unlikely(!txb))
		return;
	for (i = 0; i < txb->nr_frags; i++) 
		if (txb->fragments[i])
			dev_kfree_skb_any(txb->fragments[i]);
	kfree(txb);
	
}

struct ieee80211_txb *ieee80211_r8180_alloc_txb(int nr_frags, int txb_size,
					  int gfp_mask) {
	struct ieee80211_txb *txb;
	int i;
	//static int dealloc=0;
	txb = (struct ieee80211_txb *)kmalloc(
		sizeof(struct ieee80211_txb) + (sizeof(u8*) * nr_frags), 
		gfp_mask);
	if (!txb)
		return NULL;

	memset(txb, 0, sizeof(struct ieee80211_txb));
	txb->nr_frags = nr_frags;
	txb->frag_size = txb_size;

	for (i = 0; i < nr_frags; i++) {
		txb->fragments[i] = dev_alloc_skb(txb_size);
		if (unlikely(!txb->fragments[i])) {
			i--;
			break;
		}
	}
	if (unlikely(i != nr_frags)) {
		while (i >= 0)
			dev_kfree_skb_any(txb->fragments[i--]);
		kfree(txb);
		IEEE80211DMESG("fail");
		return NULL;
	}
	
	return txb;
}


u16 ieee80211_calc_duration(u32 len, short rate)//,short* ext)
{
	//*ext=0;
	u16 duration;
	u16 drift;
	switch(rate){
	case 0://1mbps
	//	*ext=0;
		duration = ((len+4)<<4) /0x2;
		drift = ((len+4)<<4) % 0x2;
		if(drift ==0 ) break;
		duration++;
		break;
		
	case 1://2mbps
	//	*ext=0;
		duration = ((len+4)<<4) /0x4;
		drift = ((len+4)<<4) % 0x4;
		if(drift ==0 ) break;
		duration++;
		break;
		
	case 2: //5.5mbps
	//	*ext=0;
		duration = ((len+4)<<4) /0xb;
		drift = ((len+4)<<4) % 0xb;
		if(drift ==0 ) 
			break;
		duration++;
		break;
		
	default:
	case 3://11mbps				
	//	*ext=0;
		duration = ((len+4)<<4) /0x16;
		drift = ((len+4)<<4) % 0x16;
		if(drift ==0 ) 
			break;
		duration++;
	/*	if(drift > 6) 
			break;
		*ext=1;*/
		break;
	}
	
	return duration;
}



struct ieee80211_txb *ieee80211_r8180_skb_to_txb(struct ieee80211_device *ieee, 
					   struct sk_buff *skb)
{
	struct ieee80211_txb *txb;
	int i, payload_size, nr_frags, last_payload_size;
	//unsigned long flags;
	struct net_device_stats *stats = &ieee->stats;
	int ether_type, encrypt;
	int size, fc, hdr_len;
	struct sk_buff *skb_frag;
	struct ieee80211_header_data *header;
	u8 src_addr[ETH_ALEN];
	u8 dst_addr[ETH_ALEN];
	u16 acktime;
	u8 icv[]={0,0,0,0};
	
#ifndef CONFIG_IEEE80211_NOWEP
	struct ieee80211_crypt_data* crypt;
#endif
	/* we don't aquire lock since this is called only from 
	 * already-locked context
	 */	
	
	//spin_lock_irqsave(&ieee->lock, flags);
	
	
	/* We do not return fail even if not associated,
	 * during reassociation data frame are accepted until
	 * the TX buffer is full. The nic should avoid to TX
	 * them due to HW disable of the dma from data ring
	 * buffer. As far as I know frames in the NIC fifo 
	 * are TXed without possibility of prevent it.
	 * On the other hand when the nic is totally unassociated
	 * the kernel queue should be stopped.
	 */
	
	//if(ieee->link_state != WLAN_LINK_ASSOCIATED) goto failed;
	
	if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
		printk(KERN_WARNING "%s: skb too small (%d).\n",
		       ieee->dev->name, skb->len);
		goto failed;
	}
#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("going to copy adr from 8023");
#endif
	memcpy(dst_addr, skb->data, ETH_ALEN);
	memcpy(src_addr, skb->data + ETH_ALEN, ETH_ALEN);
	ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("copy adr from 8023 done");
#endif
	if(ieee->iw_mode == IW_MODE_MASTER)
		add_bridged(ieee,src_addr);
#ifndef CONFIG_IEEE80211_NOWEP
	crypt = ieee->crypt[ieee->tx_keyidx];
	encrypt = (ieee->host_encrypt && ether_type != ETH_P_PAE && 
		   crypt && crypt->ops);
#else
	encrypt = 0;
#endif /* CONFIG_IEEE80211_NOWEP */

	/* Advance the SKB to the start of the payload */
	skb_pull(skb, sizeof(struct ethhdr));

	/* Determine total amount of storage required for TXB packets excluded headers*/
	size = skb->len + SNAP_SIZE + sizeof(u16);

#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("calc payload per frag");
#endif

	/* Determine amount of data per fragment */
	if(ieee->hw_wep)
		payload_size = ieee->fts-8;
	else
		payload_size = ieee->fts;
	if (!ieee->tx_payload_only) 
		payload_size -= sizeof(struct ieee80211_header_data);
#ifndef CONFIG_IEEE80211_NOWEP	
	if (encrypt)
		payload_size -= crypt->ops->extra_prefix_len +
			crypt->ops->extra_postfix_len;
#endif /* CONFIG_IEEE80211_NOWEP */
	nr_frags = size / payload_size;
	last_payload_size = size % payload_size;
	
	if (last_payload_size)
		nr_frags++;
	else
		last_payload_size = payload_size;

	/* When we allocate the TXB we allocate enough space for the reserve
	 * and full fragment size (payload_size doesn't include prefix and 
	 * postfix) */
#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("going to alloc txb");
#endif

	txb = ieee80211_r8180_alloc_txb(nr_frags, ieee->fts, GFP_ATOMIC);
	if (unlikely(!txb)) {
		printk(KERN_WARNING "%s: Could not allocate TXB\n",
		       ieee->dev->name);
		goto failed;
	}
	txb->encrypted = encrypt;
	txb->payload_size = size;

#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("entering frag loop");
#endif

	for (i = 0; i < nr_frags; i++) {
		skb_frag = txb->fragments[i];
#ifdef DEBUG_SKB2TXB
		IEEE80211DMESG("frag");
#endif

#ifndef CONFIG_IEEE80211_NOWEP	
		if (encrypt) 
			skb_reserve(skb_frag, crypt->ops->extra_prefix_len);
#endif /* CONFIG_IEEE80211_NOWEP */
		
		size = (i == nr_frags - 1) ? last_payload_size : payload_size;
		
		if (!ieee->tx_payload_only) {
			header = (struct ieee80211_header_data *)
				skb_put(skb_frag, sizeof(struct ieee80211_header_data));
			switch(ieee->iw_mode){
				case IW_MODE_INFRA:
				memcpy(header->addr3 ,dst_addr,ETH_ALEN);
				memcpy(header->addr1 ,ieee->ass_beacon->bssid,ETH_ALEN);
				memcpy(header->addr2 ,src_addr,ETH_ALEN);
				break;
			
				case IW_MODE_MASTER:
				memcpy(header->addr1 ,dst_addr,ETH_ALEN);
				memcpy(header->addr2 ,ieee->beacon_cell_ssid,ETH_ALEN);
				memcpy(header->addr3 ,src_addr,ETH_ALEN);
				
				break;
				case IW_MODE_ADHOC: 
				memcpy(header->addr1 ,dst_addr,ETH_ALEN);
				memcpy(header->addr3 ,ieee->beacon_cell_ssid,ETH_ALEN);
				memcpy(header->addr2 ,src_addr,ETH_ALEN);
				break;
				
				
			}
			/* 
			 LOWORD(fc) = 0x08 for data, =0x18 for data+ack 
			 if NO pwr-mngmt HIWORD(fc) =0x1 for last frag, =0x5 for more frag 
			*/
#ifdef DEBUG_SKB2TXB
			IEEE80211DMESG("80211 addr copied");
#endif

			header->frame_control = 8; 
			if(i+1<nr_frags) header->frame_control |= 0x0400;
			//else header->frame_control =0x0108;
			if(ieee->iw_mode == IW_MODE_INFRA) header->frame_control |= (1<<8);
			else if(ieee->iw_mode == IW_MODE_MASTER) 
				header->frame_control |= (1<<9);
			
			if(ieee->hw_wep || encrypt) header->frame_control |= 0x4000;
			header->seq_ctrl= i;
			header->seq_ctrl|= ieee->seq_ctrl<<4;
			
			header->seq_ctrl= cpu_to_le16(header->seq_ctrl);
						
			/*printk(KERN_DEBUG "%s: TODO -- implement 802.11 "
			       "header construction...\n", ieee->dev->name);
			*/
			fc = le16_to_cpu(header->frame_control);
			hdr_len = ieee80211_get_hdrlen(fc);
#ifdef DEBUG_SKB2TXB
			IEEE80211DMESG("going to calc ACKtime");
#endif
	
			acktime = ieee80211_calc_duration(ACK_LEN,ieee->rate);
			header->duration_id= is_broadcast(header->addr1) ? 0 : (
				( i == nr_frags - 1) ? 
					acktime + ieee->sifs_time : 
					
					2*acktime + 3*ieee->sifs_time + 
					hdr_len +
					( i == nr_frags - 2) ? last_payload_size : payload_size
				); 
			header->duration_id= cpu_to_le16(header->duration_id);
			header->duration_id =0;
			
		} else 
			hdr_len = 0;
		
		
		//size-=hdr_len;
		if(ieee->hw_wep) skb_put(skb_frag,4); //advance skb to provide IV+key room
		/* Put a SNAP header on the first fragment */
		if (i == 0) {
			
			ieee80211_put_snap(
				skb_put(skb_frag, SNAP_SIZE + sizeof(u16)), 
				ether_type);
			size -= SNAP_SIZE + sizeof(u16);
		}
		
		
		memcpy(skb_put(skb_frag, size), skb->data, size);
		if(ieee->hw_wep) memcpy(skb_put(skb_frag,4),icv,4); //advance skb to provide ICV room
		/* Advance the SKB... */
		skb_pull(skb, size);

#ifndef CONFIG_IEEE80211_NOWEP
		/* Encryption routine will move the header forward in order
		 * to insert the IV between the header and the payload */
		if (encrypt)
			ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
#endif
	}

#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("Exiting from frag loop");
#endif

	if(!ieee->hw_seq)
	{
		if(ieee->seq_ctrl == 0xFFF) ieee->seq_ctrl=0;
		else {
			ieee->seq_ctrl++;
			//IEEE80211DMESG("SW number wrapped back");
		}
	}
	/* We are now done with the SKB provided to us */
	dev_kfree_skb_any(skb);
	
	//spin_unlock_irqrestore(&ieee->lock, flags);
#ifdef DEBUG_SKB2TXB
	IEEE80211DMESG("Exiting from skb2txb");
#endif
	return txb;

 failed:
	stats->tx_errors++;
	//spin_unlock_irqrestore(&ieee->lock, flags);
	return NULL;
}

int ieee80211_r8180_8023_hardstartxmit(struct sk_buff *skb, struct ieee80211_device *ieee)
{
	struct ieee80211_txb *txb;
	
	unsigned long flags;
	int  i;
	int err=0;
	
	spin_lock_irqsave(&ieee->lock,flags);
	
	if(ieee->queue_stop){
		IEEE80211DMESG("EE: IEEE hard_start_xmit invoked when kernel queue should be stopped");
		netif_stop_queue(ieee->dev);
		ieee->ieee_stats.swtxstop++;
		//dev_kfree_skb_any(skb);
		err = 1;
		goto exit;
	}
	
	ieee->stats.tx_bytes+=skb->len;
	txb=ieee80211_r8180_skb_to_txb(ieee,skb);
	if(txb==NULL){
		IEEE80211DMESG("WW: IEEE stack failed to provide txb");
		//dev_kfree_skb_any(skb);
		err = 1;
		goto exit;
	}
	 
	for(i=0; i<txb->nr_frags; i++) {
	
		if(ieee->queue_stop){
			ieee->tx_pending.txb = txb;
			ieee->tx_pending.frag = i;
			goto exit;
		}else{
#ifdef DEBUG_HARD_XMIT
		IEEE80211DMESG("going to call the TX callback");
#endif

			ieee->func->hard_data_xmit(ieee->dev,
				txb->fragments[i],(i+1)<txb->nr_frags);
			ieee->stats.tx_packets++;
			ieee->dev->trans_start = jiffies; 
		}
	}	
	
#ifdef DEBUG_HARD_XMIT
	IEEE80211DMESG("Going to free txb");
#endif
	ieee80211_r8180_txb_free(txb);
	
	exit:
	spin_unlock_irqrestore(&ieee->lock,flags);
	
	return err;
}


void ieee80211_r8180_resume_tx(struct ieee80211_device *ieee)
{
	int i;
	for(i=ieee->tx_pending.frag; i<ieee->tx_pending.txb->nr_frags; i++) {
		
		if(ieee->queue_stop){
			ieee->tx_pending.frag = i;
			return;
		}else{
		
			ieee->func->hard_data_xmit(ieee->dev, 
				ieee->tx_pending.txb->fragments[i],
				(i+1)<ieee->tx_pending.txb->nr_frags);
			ieee->stats.tx_packets++;
			ieee->dev->trans_start = jiffies;
		}
	}
	
	
	ieee80211_r8180_txb_free(ieee->tx_pending.txb);
	ieee->tx_pending.txb=NULL;
}


void ieee80211_r8180_reset_queue(struct ieee80211_device *ieee)
{
	if(ieee->tx_pending.txb){
		ieee80211_r8180_txb_free(ieee->tx_pending.txb);
		ieee->tx_pending.txb=NULL;
	}
	ieee->queue_stop = 0;
	

}

void ieee80211_r8180_wake_queue(struct ieee80211_device *ieee)
{

	unsigned long flags;
	
	spin_lock_irqsave(&ieee->lock,flags);
	if(! ieee->queue_stop) goto exit;
	
	ieee->queue_stop = 0;
	
	if(ieee->tx_pending.txb)
		ieee80211_r8180_resume_tx(ieee);
	
	if(!ieee->queue_stop && netif_queue_stopped(ieee->dev)){
		ieee->ieee_stats.swtxawake++;
		netif_wake_queue(ieee->dev);
	}
	
	exit :
	spin_unlock_irqrestore(&ieee->lock,flags);
}


void ieee80211_r8180_stop_queue(struct ieee80211_device *ieee)
{
	if(! netif_queue_stopped(ieee->dev)){
		netif_stop_queue(ieee->dev);
		ieee->ieee_stats.swtxstop++;
	}
	ieee->queue_stop = 1;
	
}

EXPORT_SYMBOL(ieee80211_r8180_8023_hardstartxmit);
//EXPORT_SYMBOL(ieee80211_r8180_skb_to_txb);
EXPORT_SYMBOL(ieee80211_r8180_wake_queue);
EXPORT_SYMBOL(ieee80211_r8180_stop_queue);
//EXPORT_SYMBOL(ieee80211_r8180_txb_free);
//EXPORT_SYMBOL(ieee80211_r8180_alloc_txb);
EXPORT_SYMBOL(ieee80211_r8180_reset_queue);
