/*
   This is part of rtl818x pci OpenSource driver - v 0.1
   Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
   Released under the terms of GPL (General Public License)

   Parts of this driver are based on the GPL part of the official
   Realtek driver.

   Parts of this driver are based on the rtl8180 driver skeleton
   from Patric Schenke & Andres Salomon.

   Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver.

   Parts of BB/RF code are derived from David Young rtl8180 netbsd driver.

   RSSI calc function from 'The Deuce'

   Some ideas borrowed from the 8139too.c driver included in linux kernel.

   We (I?) want to thanks the Authors of those projecs and also the
   Ndiswrapper's project Authors.

   A big big thanks goes also to Realtek corp. for their help in my attempt to
   add RTL8185 and RTL8225 support, and to David Young also.

   Power management interface routines.
   Written by Mariusz Matuszek.
*/

#undef RX_DONT_PASS_UL
#undef DUMMY_RX

#include <linux/syscalls.h>

#include "r8180_hw.h"
#include "r8180.h"
#include "r8180_rtl8225.h" /* RTL8225 Radio frontend */
#include "r8180_93cx6.h"   /* Card EEPROM */
#include "r8180_wx.h"
#include "r8180_dm.h"

#include "ieee80211/dot11d.h"

#ifndef PCI_VENDOR_ID_BELKIN
	#define PCI_VENDOR_ID_BELKIN 0x1799
#endif
#ifndef PCI_VENDOR_ID_DLINK
	#define PCI_VENDOR_ID_DLINK 0x1186
#endif

static struct pci_device_id rtl8180_pci_id_tbl[] __devinitdata = {
        {
                .vendor = PCI_VENDOR_ID_REALTEK,
                .device = 0x8199,
                .subvendor = PCI_ANY_ID,
                .subdevice = PCI_ANY_ID,
                .driver_data = 0,
        },
        {
                .vendor = 0,
                .device = 0,
                .subvendor = 0,
                .subdevice = 0,
                .driver_data = 0,
        }
};


static char* ifname = "wlan%d";
static int hwseqnum = 0;
static int hwwep = 0;
static int channels = 0x3fff;

#define eqMacAddr(a,b)		( ((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3] && (a)[4]==(b)[4] && (a)[5]==(b)[5]) ? 1:0 )
#define cpMacAddr(des,src)	      ((des)[0]=(src)[0],(des)[1]=(src)[1],(des)[2]=(src)[2],(des)[3]=(src)[3],(des)[4]=(src)[4],(des)[5]=(src)[5])
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, rtl8180_pci_id_tbl);
MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
MODULE_DESCRIPTION("Linux driver for Realtek RTL8180 / RTL8185 WiFi cards");


module_param(ifname, charp, S_IRUGO|S_IWUSR );
module_param(hwseqnum,int, S_IRUGO|S_IWUSR);
module_param(hwwep,int, S_IRUGO|S_IWUSR);
module_param(channels,int, S_IRUGO|S_IWUSR);

MODULE_PARM_DESC(devname," Net interface name, wlan%d=default");
MODULE_PARM_DESC(hwseqnum," Try to use hardware 802.11 header sequence numbers. Zero=default");
MODULE_PARM_DESC(hwwep," Try to use hardware WEP support. Still broken and not available on all cards");
MODULE_PARM_DESC(channels," Channel bitmask for specific locales. NYI");


static int __devinit rtl8180_pci_probe(struct pci_dev *pdev,
				       const struct pci_device_id *id);

static void __devexit rtl8180_pci_remove(struct pci_dev *pdev);

static void rtl8180_shutdown (struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	if (dev->netdev_ops->ndo_stop)
		dev->netdev_ops->ndo_stop(dev);
	pci_disable_device(pdev);
}

static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state)
{
	struct net_device *dev = pci_get_drvdata(pdev);

	if (!netif_running(dev))
		goto out_pci_suspend;

	if (dev->netdev_ops->ndo_stop)
		dev->netdev_ops->ndo_stop(dev);

	netif_device_detach(dev);

out_pci_suspend:
	pci_save_state(pdev);
	pci_disable_device(pdev);
	pci_set_power_state(pdev, pci_choose_state(pdev, state));
	return 0;
}

static int rtl8180_resume(struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	int err;
	u32 val;

	pci_set_power_state(pdev, PCI_D0);

	err = pci_enable_device(pdev);
	if (err) {
		printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
				dev->name);

		return err;
	}

	pci_restore_state(pdev);

	/*
	 * Suspend/Resume resets the PCI configuration space, so we have to
	 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
	 * from interfering with C3 CPU state. pci_restore_state won't help
	 * here since it only restores the first 64 bytes pci config header.
	 */
	pci_read_config_dword(pdev, 0x40, &val);
	if ((val & 0x0000ff00) != 0)
		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);

	if (!netif_running(dev))
		goto out;

	if (dev->netdev_ops->ndo_open)
		dev->netdev_ops->ndo_open(dev);

	netif_device_attach(dev);
out:
	return 0;
}

static struct pci_driver rtl8180_pci_driver = {
	.name		= RTL8180_MODULE_NAME,
	.id_table	= rtl8180_pci_id_tbl,
	.probe		= rtl8180_pci_probe,
	.remove		= __devexit_p(rtl8180_pci_remove),
	.suspend	= rtl8180_suspend,
	.resume		= rtl8180_resume,
	.shutdown	= rtl8180_shutdown,
};

u8 read_nic_byte(struct net_device *dev, int x)
{
        return 0xff&readb((u8*)dev->mem_start +x);
}

u32 read_nic_dword(struct net_device *dev, int x)
{
        return readl((u8*)dev->mem_start +x);
}

u16 read_nic_word(struct net_device *dev, int x)
{
        return readw((u8*)dev->mem_start +x);
}

void write_nic_byte(struct net_device *dev, int x,u8 y)
{
        writeb(y,(u8*)dev->mem_start +x);
	udelay(20);
}

void write_nic_dword(struct net_device *dev, int x,u32 y)
{
        writel(y,(u8*)dev->mem_start +x);
	udelay(20);
}

void write_nic_word(struct net_device *dev, int x,u16 y)
{
        writew(y,(u8*)dev->mem_start +x);
	udelay(20);
}

inline void force_pci_posting(struct net_device *dev)
{
	read_nic_byte(dev,EPROM_CMD);
	mb();
}

irqreturn_t rtl8180_interrupt(int irq, void *netdev, struct pt_regs *regs);
void set_nic_rxring(struct net_device *dev);
void set_nic_txring(struct net_device *dev);
static struct net_device_stats *rtl8180_stats(struct net_device *dev);
void rtl8180_commit(struct net_device *dev);
void rtl8180_start_tx_beacon(struct net_device *dev);

static struct proc_dir_entry *rtl8180_proc = NULL;

static int proc_get_registers(char *page, char **start,
			  off_t offset, int count,
			  int *eof, void *data)
{
	struct net_device *dev = data;
	int len = 0;
	int i,n;
	int max = 0xff;

	/* This dump the current register page */
	for (n = 0; n <= max;) {
		len += snprintf(page + len, count - len, "\nD:  %2x > ", n);

		for (i = 0; i < 16 && n <= max; i++, n++)
			len += snprintf(page + len, count - len, "%2x ",
					read_nic_byte(dev, n));
	}
	len += snprintf(page + len, count - len,"\n");

	*eof = 1;
	return len;
}

int get_curr_tx_free_desc(struct net_device *dev, int priority);

static int proc_get_stats_hw(char *page, char **start,
			  off_t offset, int count,
			  int *eof, void *data)
{
	int len = 0;

	*eof = 1;
	return len;
}

static int proc_get_stats_rx(char *page, char **start,
			  off_t offset, int count,
			  int *eof, void *data)
{
	struct net_device *dev = data;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	int len = 0;

	len += snprintf(page + len, count - len,
		"RX OK: %lu\n"
		"RX Retry: %lu\n"
		"RX CRC Error(0-500): %lu\n"
		"RX CRC Error(500-1000): %lu\n"
		"RX CRC Error(>1000): %lu\n"
		"RX ICV Error: %lu\n",
		priv->stats.rxint,
		priv->stats.rxerr,
		priv->stats.rxcrcerrmin,
		priv->stats.rxcrcerrmid,
		priv->stats.rxcrcerrmax,
		priv->stats.rxicverr
		);

	*eof = 1;
	return len;
}

static int proc_get_stats_tx(char *page, char **start,
			  off_t offset, int count,
			  int *eof, void *data)
{
	struct net_device *dev = data;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	int len = 0;
	unsigned long totalOK;

	totalOK=priv->stats.txnpokint+priv->stats.txhpokint+priv->stats.txlpokint;
	len += snprintf(page + len, count - len,
		"TX OK: %lu\n"
		"TX Error: %lu\n"
		"TX Retry: %lu\n"
		"TX beacon OK: %lu\n"
		"TX beacon error: %lu\n",
		totalOK,
		priv->stats.txnperr+priv->stats.txhperr+priv->stats.txlperr,
		priv->stats.txretry,
		priv->stats.txbeacon,
		priv->stats.txbeaconerr
	);

	*eof = 1;
	return len;
}

void rtl8180_proc_module_init(void)
{
	DMESG("Initializing proc filesystem");
        rtl8180_proc=create_proc_entry(RTL8180_MODULE_NAME, S_IFDIR, init_net.proc_net);
}

void rtl8180_proc_module_remove(void)
{
        remove_proc_entry(RTL8180_MODULE_NAME, init_net.proc_net);
}

void rtl8180_proc_remove_one(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	if (priv->dir_dev) {
		remove_proc_entry("stats-hw", priv->dir_dev);
		remove_proc_entry("stats-tx", priv->dir_dev);
		remove_proc_entry("stats-rx", priv->dir_dev);
		remove_proc_entry("registers", priv->dir_dev);
		remove_proc_entry(dev->name, rtl8180_proc);
		priv->dir_dev = NULL;
	}
}

void rtl8180_proc_init_one(struct net_device *dev)
{
	struct proc_dir_entry *e;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	priv->dir_dev = rtl8180_proc;
	if (!priv->dir_dev) {
		DMESGE("Unable to initialize /proc/net/r8180/%s\n",
		      dev->name);
		return;
	}

	e = create_proc_read_entry("stats-hw", S_IFREG | S_IRUGO,
				   priv->dir_dev, proc_get_stats_hw, dev);
	if (!e) {
		DMESGE("Unable to initialize "
		      "/proc/net/r8180/%s/stats-hw\n",
		      dev->name);
	}

	e = create_proc_read_entry("stats-rx", S_IFREG | S_IRUGO,
				   priv->dir_dev, proc_get_stats_rx, dev);
	if (!e) {
		DMESGE("Unable to initialize "
		      "/proc/net/r8180/%s/stats-rx\n",
		      dev->name);
	}


	e = create_proc_read_entry("stats-tx", S_IFREG | S_IRUGO,
				   priv->dir_dev, proc_get_stats_tx, dev);
	if (!e) {
		DMESGE("Unable to initialize "
		      "/proc/net/r8180/%s/stats-tx\n",
		      dev->name);
	}

	e = create_proc_read_entry("registers", S_IFREG | S_IRUGO,
				   priv->dir_dev, proc_get_registers, dev);
	if (!e) {
		DMESGE("Unable to initialize "
		      "/proc/net/r8180/%s/registers\n",
		      dev->name);
	}
}

/*
  FIXME: check if we can use some standard already-existent
  data type+functions in kernel
*/

short buffer_add(struct buffer **buffer, u32 *buf, dma_addr_t dma,
		struct buffer **bufferhead)
{
        struct buffer *tmp;

	if(! *buffer){

		*buffer = kmalloc(sizeof(struct buffer),GFP_KERNEL);

		if (*buffer == NULL) {
			DMESGE("Failed to kmalloc head of TX/RX struct");
			return -1;
		}
		(*buffer)->next=*buffer;
		(*buffer)->buf=buf;
		(*buffer)->dma=dma;
		if(bufferhead !=NULL)
			(*bufferhead) = (*buffer);
		return 0;
	}
	tmp=*buffer;

	while(tmp->next!=(*buffer)) tmp=tmp->next;
	if ((tmp->next= kmalloc(sizeof(struct buffer),GFP_KERNEL)) == NULL){
		DMESGE("Failed to kmalloc TX/RX struct");
		return -1;
	}
	tmp->next->buf=buf;
	tmp->next->dma=dma;
	tmp->next->next=*buffer;

	return 0;
}

void buffer_free(struct net_device *dev,struct buffer **buffer,int len,short
consistent)
{

	struct buffer *tmp,*next;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct pci_dev *pdev=priv->pdev;

	if (!*buffer)
		return;

	tmp = *buffer;

	do{
		next=tmp->next;
		if(consistent){
			pci_free_consistent(pdev,len,
				    tmp->buf,tmp->dma);
		}else{
			pci_unmap_single(pdev, tmp->dma,
			len,PCI_DMA_FROMDEVICE);
			kfree(tmp->buf);
		}
		kfree(tmp);
		tmp = next;
	}
	while(next != *buffer);

	*buffer=NULL;
}

void print_buffer(u32 *buffer, int len)
{
	int i;
	u8 *buf =(u8*)buffer;

	printk("ASCII BUFFER DUMP (len: %x):\n",len);

	for(i=0;i<len;i++)
		printk("%c",buf[i]);

	printk("\nBINARY BUFFER DUMP (len: %x):\n",len);

	for(i=0;i<len;i++)
		printk("%02x",buf[i]);

	printk("\n");
}

int get_curr_tx_free_desc(struct net_device *dev, int priority)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u32* tail;
	u32* head;
	int ret;

	switch (priority){
		case MANAGE_PRIORITY:
			head = priv->txmapringhead;
			tail = priv->txmapringtail;
			break;
		case BK_PRIORITY:
			head = priv->txbkpringhead;
			tail = priv->txbkpringtail;
			break;
		case BE_PRIORITY:
			head = priv->txbepringhead;
			tail = priv->txbepringtail;
			break;
		case VI_PRIORITY:
			head = priv->txvipringhead;
			tail = priv->txvipringtail;
			break;
		case VO_PRIORITY:
			head = priv->txvopringhead;
			tail = priv->txvopringtail;
			break;
		case HI_PRIORITY:
			head = priv->txhpringhead;
			tail = priv->txhpringtail;
			break;
		default:
			return -1;
	}

	if (head <= tail)
		ret = priv->txringcount - (tail - head)/8;
	else
		ret = (head - tail)/8;

	if (ret > priv->txringcount)
		DMESG("BUG");

	return ret;
}

short check_nic_enought_desc(struct net_device *dev, int priority)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	struct ieee80211_device *ieee = netdev_priv(dev);
	int requiredbyte, required;

	requiredbyte = priv->ieee80211->fts + sizeof(struct ieee80211_header_data);

	if (ieee->current_network.QoS_Enable)
		requiredbyte += 2;

	required = requiredbyte / (priv->txbuffsize-4);

	if (requiredbyte % priv->txbuffsize)
		required++;

	/* for now we keep two free descriptor as a safety boundary
	 * between the tail and the head
	 */

	return (required+2 < get_curr_tx_free_desc(dev,priority));
}

void fix_tx_fifo(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	u32 *tmp;
	int i;

	for (tmp=priv->txmapring, i=0;
	     i < priv->txringcount;
	     tmp+=8, i++){
		*tmp = *tmp &~ (1<<31);
	}

	for (tmp=priv->txbkpring, i=0;
	     i < priv->txringcount;
	     tmp+=8, i++) {
		*tmp = *tmp &~ (1<<31);
	}

	for (tmp=priv->txbepring, i=0;
	     i < priv->txringcount;
	     tmp+=8, i++){
		*tmp = *tmp &~ (1<<31);
	}
	for (tmp=priv->txvipring, i=0;
	     i < priv->txringcount;
	     tmp+=8, i++) {
		*tmp = *tmp &~ (1<<31);
	}

	for (tmp=priv->txvopring, i=0;
	     i < priv->txringcount;
	     tmp+=8, i++){
		*tmp = *tmp &~ (1<<31);
	}

	for (tmp=priv->txhpring, i=0;
	     i < priv->txringcount;
	     tmp+=8,i++){
		*tmp = *tmp &~ (1<<31);
	}

	for (tmp=priv->txbeaconring, i=0;
	     i < priv->txbeaconcount;
	     tmp+=8, i++){
		*tmp = *tmp &~ (1<<31);
	}

	priv->txmapringtail = priv->txmapring;
	priv->txmapringhead = priv->txmapring;
	priv->txmapbufstail = priv->txmapbufs;

	priv->txbkpringtail = priv->txbkpring;
	priv->txbkpringhead = priv->txbkpring;
	priv->txbkpbufstail = priv->txbkpbufs;

	priv->txbepringtail = priv->txbepring;
	priv->txbepringhead = priv->txbepring;
	priv->txbepbufstail = priv->txbepbufs;

	priv->txvipringtail = priv->txvipring;
	priv->txvipringhead = priv->txvipring;
	priv->txvipbufstail = priv->txvipbufs;

	priv->txvopringtail = priv->txvopring;
	priv->txvopringhead = priv->txvopring;
	priv->txvopbufstail = priv->txvopbufs;

	priv->txhpringtail = priv->txhpring;
	priv->txhpringhead = priv->txhpring;
	priv->txhpbufstail = priv->txhpbufs;

	priv->txbeaconringtail = priv->txbeaconring;
	priv->txbeaconbufstail = priv->txbeaconbufs;
	set_nic_txring(dev);

	ieee80211_reset_queue(priv->ieee80211);
	priv->ack_tx_to_ieee = 0;
}

void fix_rx_fifo(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	u32 *tmp;
	struct buffer *rxbuf;
	u8 rx_desc_size;

	rx_desc_size = 8; // 4*8 = 32 bytes

	for (tmp=priv->rxring, rxbuf=priv->rxbufferhead;
	     (tmp < (priv->rxring)+(priv->rxringcount)*rx_desc_size);
	     tmp+=rx_desc_size,rxbuf=rxbuf->next){
		*(tmp+2) = rxbuf->dma;
		*tmp=*tmp &~ 0xfff;
		*tmp=*tmp | priv->rxbuffersize;
		*tmp |= (1<<31);
	}

	priv->rxringtail=priv->rxring;
	priv->rxbuffer=priv->rxbufferhead;
	priv->rx_skb_complete=1;
	set_nic_rxring(dev);
}

unsigned char QUALITY_MAP[] = {
	0x64, 0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x61,
	0x61, 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5d, 0x5c,
	0x5b, 0x5a, 0x59, 0x57, 0x56, 0x54, 0x52, 0x4f,
	0x4c, 0x49, 0x45, 0x41, 0x3c, 0x37, 0x31, 0x29,
	0x24, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
	0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20,
	0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e,
	0x1d, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, 0x19, 0x19,
	0x18, 0x17, 0x16, 0x15, 0x14, 0x12, 0x11, 0x0f,
	0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x01, 0x00
};

unsigned char STRENGTH_MAP[] = {
	0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e,
	0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50,
	0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f,
	0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b,
	0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17,
	0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13,
	0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f,
	0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b,
	0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07,
	0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x00
};

void rtl8180_RSSI_calc(struct net_device *dev, u8 *rssi, u8 *qual)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	u32 temp;
	u32 temp2;
	u32 temp3;
	u32 lsb;
	u32 q;
	u32 orig_qual;
	u8  _rssi;

	q = *qual;
	orig_qual = *qual;
	_rssi = 0; // avoid gcc complains..

	if (q <= 0x4e) {
		temp = QUALITY_MAP[q];
	} else {
		if( q & 0x80 ) {
			temp = 0x32;
		} else {
			temp = 1;
		}
	}

	*qual = temp;
	temp2 = *rssi;

	switch(priv->rf_chip){
	case RFCHIPID_RFMD:
		lsb = temp2 & 1;
		temp2 &= 0x7e;
		if ( !lsb || !(temp2 <= 0x3c) ) {
			temp2 = 0x64;
		} else {
			temp2 = 100 * temp2 / 0x3c;
		}
		*rssi = temp2 & 0xff;
		_rssi = temp2 & 0xff;
		break;
	case RFCHIPID_INTERSIL:
		lsb = temp2;
		temp2 &= 0xfffffffe;
		temp2 *= 251;
		temp3 = temp2;
		temp2 <<= 6;
		temp3 += temp2;
		temp3 <<= 1;
		temp2 = 0x4950df;
		temp2 -= temp3;
		lsb &= 1;
		if ( temp2 <= 0x3e0000 ) {
			if ( temp2 < 0xffef0000 )
				temp2 = 0xffef0000;
		} else {
			temp2 = 0x3e0000;
		}
		if ( !lsb ) {
			temp2 -= 0xf0000;
		} else {
			temp2 += 0xf0000;
		}

		temp3 = 0x4d0000;
		temp3 -= temp2;
		temp3 *= 100;
		temp3 = temp3 / 0x6d;
		temp3 >>= 0x10;
		_rssi = temp3 & 0xff;
		*rssi = temp3 & 0xff;
		break;
	case RFCHIPID_GCT:
	        lsb = temp2 & 1;
		temp2 &= 0x7e;
		if ( ! lsb || !(temp2 <= 0x3c) ){
			temp2 = 0x64;
		} else {
			temp2 = (100 * temp2) / 0x3c;
		}
		*rssi = temp2 & 0xff;
		_rssi = temp2 & 0xff;
		break;
	case RFCHIPID_PHILIPS:
		if( orig_qual <= 0x4e ){
			_rssi = STRENGTH_MAP[orig_qual];
			*rssi = _rssi;
		} else {
			orig_qual -= 0x80;
			if ( !orig_qual ){
				_rssi = 1;
				*rssi = 1;
			} else {
				_rssi = 0x32;
				*rssi = 0x32;
			}
		}
		break;
	case RFCHIPID_MAXIM:
		lsb = temp2 & 1;
		temp2 &= 0x7e;
		temp2 >>= 1;
		temp2 += 0x42;
		if( lsb != 0 ){
			temp2 += 0xa;
		}
		*rssi = temp2 & 0xff;
		_rssi = temp2 & 0xff;
		break;
	}

	if ( _rssi < 0x64 ){
		if ( _rssi == 0 ) {
			*rssi = 1;
		}
	} else {
		*rssi = 0x64;
	}

	return;
}

void rtl8180_irq_enable(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	priv->irq_enabled = 1;
	write_nic_word(dev,INTA_MASK, priv->irq_mask);
}

void rtl8180_irq_disable(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	write_nic_dword(dev,IMR,0);
	force_pci_posting(dev);
	priv->irq_enabled = 0;
}

void rtl8180_set_mode(struct net_device *dev,int mode)
{
	u8 ecmd;

	ecmd=read_nic_byte(dev, EPROM_CMD);
	ecmd=ecmd &~ EPROM_CMD_OPERATING_MODE_MASK;
	ecmd=ecmd | (mode<<EPROM_CMD_OPERATING_MODE_SHIFT);
	ecmd=ecmd &~ (1<<EPROM_CS_SHIFT);
	ecmd=ecmd &~ (1<<EPROM_CK_SHIFT);
	write_nic_byte(dev, EPROM_CMD, ecmd);
}

void rtl8180_adapter_start(struct net_device *dev);
void rtl8180_beacon_tx_enable(struct net_device *dev);

void rtl8180_update_msr(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u8 msr;
	u32 rxconf;

	msr  = read_nic_byte(dev, MSR);
	msr &= ~ MSR_LINK_MASK;

	rxconf=read_nic_dword(dev,RX_CONF);

	if(priv->ieee80211->state == IEEE80211_LINKED)
	{
		if(priv->ieee80211->iw_mode == IW_MODE_ADHOC)
			msr |= (MSR_LINK_ADHOC<<MSR_LINK_SHIFT);
		else if (priv->ieee80211->iw_mode == IW_MODE_MASTER)
			msr |= (MSR_LINK_MASTER<<MSR_LINK_SHIFT);
		else if (priv->ieee80211->iw_mode == IW_MODE_INFRA)
			msr |= (MSR_LINK_MANAGED<<MSR_LINK_SHIFT);
		else
			msr |= (MSR_LINK_NONE<<MSR_LINK_SHIFT);
		rxconf |= (1<<RX_CHECK_BSSID_SHIFT);

	}else {
		msr |= (MSR_LINK_NONE<<MSR_LINK_SHIFT);
		rxconf &= ~(1<<RX_CHECK_BSSID_SHIFT);
	}

	write_nic_byte(dev, MSR, msr);
	write_nic_dword(dev, RX_CONF, rxconf);
}

void rtl8180_set_chan(struct net_device *dev,short ch)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	if ((ch > 14) || (ch < 1)) {
		printk("In %s: Invalid chnanel %d\n", __func__, ch);
		return;
	}

	priv->chan=ch;
	priv->rf_set_chan(dev,priv->chan);
}

void rtl8180_rx_enable(struct net_device *dev)
{
	u8 cmd;
	u32 rxconf;
	/* for now we accept data, management & ctl frame*/
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rxconf=read_nic_dword(dev,RX_CONF);
	rxconf = rxconf &~ MAC_FILTER_MASK;
	rxconf = rxconf | (1<<ACCEPT_MNG_FRAME_SHIFT);
	rxconf = rxconf | (1<<ACCEPT_DATA_FRAME_SHIFT);
	rxconf = rxconf | (1<<ACCEPT_BCAST_FRAME_SHIFT);
	rxconf = rxconf | (1<<ACCEPT_MCAST_FRAME_SHIFT);
	if (dev->flags & IFF_PROMISC)
		DMESG("NIC in promisc mode");

	if(priv->ieee80211->iw_mode == IW_MODE_MONITOR || \
	   dev->flags & IFF_PROMISC){
		rxconf = rxconf | (1<<ACCEPT_ALLMAC_FRAME_SHIFT);
	}else{
		rxconf = rxconf | (1<<ACCEPT_NICMAC_FRAME_SHIFT);
		if(priv->card_8185 == 0)
			rxconf = rxconf | (1<<RX_CHECK_BSSID_SHIFT);
	}

	if(priv->ieee80211->iw_mode == IW_MODE_MONITOR){
		rxconf = rxconf | (1<<ACCEPT_CTL_FRAME_SHIFT);
		rxconf = rxconf | (1<<ACCEPT_ICVERR_FRAME_SHIFT);
		rxconf = rxconf | (1<<ACCEPT_PWR_FRAME_SHIFT);
	}

	if( priv->crcmon == 1 && priv->ieee80211->iw_mode == IW_MODE_MONITOR)
		rxconf = rxconf | (1<<ACCEPT_CRCERR_FRAME_SHIFT);

	rxconf = rxconf & ~RX_FIFO_THRESHOLD_MASK;
	rxconf = rxconf | (RX_FIFO_THRESHOLD_NONE << RX_FIFO_THRESHOLD_SHIFT);

	rxconf = rxconf | (1<<RX_AUTORESETPHY_SHIFT);
	rxconf = rxconf &~ MAX_RX_DMA_MASK;
	rxconf = rxconf | (MAX_RX_DMA_2048<<MAX_RX_DMA_SHIFT);

	rxconf = rxconf | RCR_ONLYERLPKT;

	rxconf = rxconf &~ RCR_CS_MASK;

	if (!priv->card_8185)
		rxconf |= (priv->rcr_csense<<RCR_CS_SHIFT);

	write_nic_dword(dev, RX_CONF, rxconf);

	fix_rx_fifo(dev);

	cmd=read_nic_byte(dev,CMD);
	write_nic_byte(dev,CMD,cmd | (1<<CMD_RX_ENABLE_SHIFT));
}

void set_nic_txring(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	write_nic_dword(dev, TX_MANAGEPRIORITY_RING_ADDR, priv->txmapringdma);
	write_nic_dword(dev, TX_BKPRIORITY_RING_ADDR, priv->txbkpringdma);
	write_nic_dword(dev, TX_BEPRIORITY_RING_ADDR, priv->txbepringdma);
	write_nic_dword(dev, TX_VIPRIORITY_RING_ADDR, priv->txvipringdma);
	write_nic_dword(dev, TX_VOPRIORITY_RING_ADDR, priv->txvopringdma);
	write_nic_dword(dev, TX_HIGHPRIORITY_RING_ADDR, priv->txhpringdma);
	write_nic_dword(dev, TX_BEACON_RING_ADDR, priv->txbeaconringdma);
}

void rtl8180_conttx_enable(struct net_device *dev)
{
	u32 txconf;

	txconf = read_nic_dword(dev,TX_CONF);
	txconf = txconf &~ TX_LOOPBACK_MASK;
	txconf = txconf | (TX_LOOPBACK_CONTINUE <<TX_LOOPBACK_SHIFT);
	write_nic_dword(dev,TX_CONF,txconf);
}

void rtl8180_conttx_disable(struct net_device *dev)
{
	u32 txconf;

	txconf = read_nic_dword(dev,TX_CONF);
	txconf = txconf &~ TX_LOOPBACK_MASK;
	txconf = txconf | (TX_LOOPBACK_NONE <<TX_LOOPBACK_SHIFT);
	write_nic_dword(dev,TX_CONF,txconf);
}

void rtl8180_tx_enable(struct net_device *dev)
{
	u8 cmd;
	u8 tx_agc_ctl;
	u8 byte;
	u32 txconf;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	txconf = read_nic_dword(dev, TX_CONF);

	if (priv->card_8185) {
		byte = read_nic_byte(dev,CW_CONF);
		byte &= ~(1<<CW_CONF_PERPACKET_CW_SHIFT);
		byte &= ~(1<<CW_CONF_PERPACKET_RETRY_SHIFT);
		write_nic_byte(dev, CW_CONF, byte);

		tx_agc_ctl = read_nic_byte(dev, TX_AGC_CTL);
		tx_agc_ctl &= ~(1<<TX_AGC_CTL_PERPACKET_GAIN_SHIFT);
		tx_agc_ctl &= ~(1<<TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT);
		tx_agc_ctl |=(1<<TX_AGC_CTL_FEEDBACK_ANT);
		write_nic_byte(dev, TX_AGC_CTL, tx_agc_ctl);
		write_nic_byte(dev, 0xec, 0x3f); /* Disable early TX */
	}

	if (priv->card_8185)
		txconf = txconf &~ (1<<TCR_PROBE_NOTIMESTAMP_SHIFT);
	else {
		if(hwseqnum)
			txconf= txconf &~ (1<<TX_CONF_HEADER_AUTOICREMENT_SHIFT);
		else
			txconf= txconf | (1<<TX_CONF_HEADER_AUTOICREMENT_SHIFT);
	}

	txconf = txconf &~ TX_LOOPBACK_MASK;
	txconf = txconf | (TX_LOOPBACK_NONE <<TX_LOOPBACK_SHIFT);
	txconf = txconf &~ TCR_DPRETRY_MASK;
	txconf = txconf &~ TCR_RTSRETRY_MASK;
	txconf = txconf | (priv->retry_data<<TX_DPRETRY_SHIFT);
	txconf = txconf | (priv->retry_rts<<TX_RTSRETRY_SHIFT);
	txconf = txconf &~ (1<<TX_NOCRC_SHIFT);

	if (priv->card_8185) {
		if (priv->hw_plcp_len)
			txconf = txconf &~ TCR_PLCP_LEN;
		else
			txconf = txconf | TCR_PLCP_LEN;
	} else
		txconf = txconf &~ TCR_SAT;

	txconf = txconf &~ TCR_MXDMA_MASK;
	txconf = txconf | (TCR_MXDMA_2048<<TCR_MXDMA_SHIFT);
	txconf = txconf | TCR_CWMIN;
	txconf = txconf | TCR_DISCW;

	txconf = txconf | (1 << TX_NOICV_SHIFT);

	write_nic_dword(dev,TX_CONF,txconf);

	fix_tx_fifo(dev);

	cmd=read_nic_byte(dev,CMD);
	write_nic_byte(dev,CMD,cmd | (1<<CMD_TX_ENABLE_SHIFT));

	write_nic_dword(dev,TX_CONF,txconf);
}

void rtl8180_beacon_tx_enable(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);
	priv->dma_poll_stop_mask &= ~(TPPOLLSTOP_BQ);
	write_nic_byte(dev,TPPollStop, priv->dma_poll_mask);
	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);
}

void rtl8180_beacon_tx_disable(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);
	priv->dma_poll_stop_mask |= TPPOLLSTOP_BQ;
	write_nic_byte(dev,TPPollStop, priv->dma_poll_stop_mask);
	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);

}

void rtl8180_rtx_disable(struct net_device *dev)
{
	u8 cmd;
	struct r8180_priv *priv = ieee80211_priv(dev);

	cmd=read_nic_byte(dev,CMD);
	write_nic_byte(dev, CMD, cmd &~ \
		       ((1<<CMD_RX_ENABLE_SHIFT)|(1<<CMD_TX_ENABLE_SHIFT)));
	force_pci_posting(dev);
	mdelay(10);

	if(!priv->rx_skb_complete)
		dev_kfree_skb_any(priv->rx_skb);
}

short alloc_tx_desc_ring(struct net_device *dev, int bufsize, int count,
			 int addr)
{
	int i;
	u32 *desc;
	u32 *tmp;
	dma_addr_t dma_desc, dma_tmp;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct pci_dev *pdev = priv->pdev;
	void *buf;

	if((bufsize & 0xfff) != bufsize) {
		DMESGE ("TX buffer allocation too large");
		return 0;
	}
	desc = (u32*)pci_alloc_consistent(pdev,
					  sizeof(u32)*8*count+256, &dma_desc);
	if (desc == NULL)
		return -1;

	if (dma_desc & 0xff)
		/*
		 * descriptor's buffer must be 256 byte aligned
		 * we shouldn't be here, since we set DMA mask !
		 */
		WARN(1, "DMA buffer is not aligned\n");

	tmp = desc;

	for (i = 0; i < count; i++) {
		buf = (void *)pci_alloc_consistent(pdev, bufsize, &dma_tmp);
		if (buf == NULL)
			return -ENOMEM;

		switch(addr) {
		case TX_MANAGEPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txmapbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer NP");
				return -ENOMEM;
			}
			break;
		case TX_BKPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txbkpbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer LP");
				return -ENOMEM;
			}
			break;
		case TX_BEPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txbepbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer NP");
				return -ENOMEM;
			}
			break;
		case TX_VIPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txvipbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer LP");
				return -ENOMEM;
			}
			break;
		case TX_VOPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txvopbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer NP");
				return -ENOMEM;
			}
			break;
		case TX_HIGHPRIORITY_RING_ADDR:
			if(-1 == buffer_add(&(priv->txhpbufs),buf,dma_tmp,NULL)){
				DMESGE("Unable to allocate mem for buffer HP");
				return -ENOMEM;
			}
			break;
		case TX_BEACON_RING_ADDR:
		        if(-1 == buffer_add(&(priv->txbeaconbufs),buf,dma_tmp,NULL)){
			DMESGE("Unable to allocate mem for buffer BP");
				return -ENOMEM;
			}
			break;
		}
		*tmp = *tmp &~ (1<<31); // descriptor empty, owned by the drv
		*(tmp+2) = (u32)dma_tmp;
		*(tmp+3) = bufsize;

		if(i+1<count)
			*(tmp+4) = (u32)dma_desc+((i+1)*8*4);
		else
			*(tmp+4) = (u32)dma_desc;

		tmp=tmp+8;
	}

	switch(addr) {
	case TX_MANAGEPRIORITY_RING_ADDR:
		priv->txmapringdma=dma_desc;
		priv->txmapring=desc;
		break;
	case TX_BKPRIORITY_RING_ADDR:
		priv->txbkpringdma=dma_desc;
		priv->txbkpring=desc;
		break;
	case TX_BEPRIORITY_RING_ADDR:
		priv->txbepringdma=dma_desc;
		priv->txbepring=desc;
		break;
	case TX_VIPRIORITY_RING_ADDR:
		priv->txvipringdma=dma_desc;
		priv->txvipring=desc;
		break;
	case TX_VOPRIORITY_RING_ADDR:
		priv->txvopringdma=dma_desc;
		priv->txvopring=desc;
		break;
	case TX_HIGHPRIORITY_RING_ADDR:
		priv->txhpringdma=dma_desc;
		priv->txhpring=desc;
		break;
	case TX_BEACON_RING_ADDR:
		priv->txbeaconringdma=dma_desc;
		priv->txbeaconring=desc;
		break;

	}

	return 0;
}

void free_tx_desc_rings(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct pci_dev *pdev=priv->pdev;
	int count = priv->txringcount;

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txmapring, priv->txmapringdma);
	buffer_free(dev,&(priv->txmapbufs),priv->txbuffsize,1);

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txbkpring, priv->txbkpringdma);
	buffer_free(dev,&(priv->txbkpbufs),priv->txbuffsize,1);

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txbepring, priv->txbepringdma);
	buffer_free(dev,&(priv->txbepbufs),priv->txbuffsize,1);

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txvipring, priv->txvipringdma);
	buffer_free(dev,&(priv->txvipbufs),priv->txbuffsize,1);

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txvopring, priv->txvopringdma);
	buffer_free(dev,&(priv->txvopbufs),priv->txbuffsize,1);

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txhpring, priv->txhpringdma);
	buffer_free(dev,&(priv->txhpbufs),priv->txbuffsize,1);

	count = priv->txbeaconcount;
	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->txbeaconring, priv->txbeaconringdma);
	buffer_free(dev,&(priv->txbeaconbufs),priv->txbuffsize,1);
}

void free_rx_desc_ring(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct pci_dev *pdev = priv->pdev;
	int count = priv->rxringcount;

	pci_free_consistent(pdev, sizeof(u32)*8*count+256,
			    priv->rxring, priv->rxringdma);

	buffer_free(dev,&(priv->rxbuffer),priv->rxbuffersize,0);
}

short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count)
{
	int i;
	u32 *desc;
	u32 *tmp;
	dma_addr_t dma_desc,dma_tmp;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct pci_dev *pdev=priv->pdev;
	void *buf;
	u8 rx_desc_size;

	rx_desc_size = 8; // 4*8 = 32 bytes

	if((bufsize & 0xfff) != bufsize){
		DMESGE ("RX buffer allocation too large");
		return -1;
	}

	desc = (u32*)pci_alloc_consistent(pdev,sizeof(u32)*rx_desc_size*count+256,
					  &dma_desc);

	if (dma_desc & 0xff)
		/*
		 * descriptor's buffer must be 256 byte aligned
		 * should never happen since we specify the DMA mask
		 */
		WARN(1, "DMA buffer is not aligned\n");

	priv->rxring=desc;
	priv->rxringdma=dma_desc;
	tmp=desc;

	for (i = 0; i < count; i++) {
		if ((buf= kmalloc(bufsize * sizeof(u8),GFP_ATOMIC)) == NULL){
			DMESGE("Failed to kmalloc RX buffer");
			return -1;
		}

		dma_tmp = pci_map_single(pdev,buf,bufsize * sizeof(u8),
					 PCI_DMA_FROMDEVICE);

		if(-1 == buffer_add(&(priv->rxbuffer), buf,dma_tmp,
			   &(priv->rxbufferhead))){
			   DMESGE("Unable to allocate mem RX buf");
			   return -1;
		}
		*tmp = 0; //zero pads the header of the descriptor
		*tmp = *tmp |( bufsize&0xfff);
		*(tmp+2) = (u32)dma_tmp;
		*tmp = *tmp |(1<<31); // descriptor void, owned by the NIC

		tmp=tmp+rx_desc_size;
	}

	*(tmp-rx_desc_size) = *(tmp-rx_desc_size) | (1<<30); // this is the last descriptor

	return 0;
}


void set_nic_rxring(struct net_device *dev)
{
	u8 pgreg;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	pgreg=read_nic_byte(dev, PGSELECT);
	write_nic_byte(dev, PGSELECT, pgreg &~ (1<<PGSELECT_PG_SHIFT));

	write_nic_dword(dev, RXRING_ADDR,priv->rxringdma);
}

void rtl8180_reset(struct net_device *dev)
{
	u8 cr;

	rtl8180_irq_disable(dev);

	cr=read_nic_byte(dev,CMD);
	cr = cr & 2;
	cr = cr | (1<<CMD_RST_SHIFT);
	write_nic_byte(dev,CMD,cr);

	force_pci_posting(dev);

	mdelay(200);

	if(read_nic_byte(dev,CMD) & (1<<CMD_RST_SHIFT))
		DMESGW("Card reset timeout!");
	else
		DMESG("Card successfully reset");

	rtl8180_set_mode(dev,EPROM_CMD_LOAD);
	force_pci_posting(dev);
	mdelay(200);
}

inline u16 ieeerate2rtlrate(int rate)
{
	switch(rate){
	case 10:
		return 0;
	case 20:
		return 1;
	case 55:
		return 2;
	case 110:
		return 3;
	case 60:
		return 4;
	case 90:
		return 5;
	case 120:
		return 6;
	case 180:
		return 7;
	case 240:
		return 8;
	case 360:
		return 9;
	case 480:
		return 10;
	case 540:
		return 11;
	default:
		return 3;
	}
}

static u16 rtl_rate[] = {10,20,55,110,60,90,120,180,240,360,480,540,720};

inline u16 rtl8180_rate2rate(short rate)
{
	if (rate > 12)
		return 10;
	return rtl_rate[rate];
}

inline u8 rtl8180_IsWirelessBMode(u16 rate)
{
	if( ((rate <= 110) && (rate != 60) && (rate != 90)) || (rate == 220) )
		return 1;
	else
		return 0;
}

u16 N_DBPSOfRate(u16 DataRate);

u16 ComputeTxTime(u16 FrameLength, u16 DataRate, u8 bManagementFrame,
		  u8 bShortPreamble)
{
	u16	FrameTime;
	u16	N_DBPS;
	u16	Ceiling;

	if (rtl8180_IsWirelessBMode(DataRate)) {
		if (bManagementFrame || !bShortPreamble || DataRate == 10)
			/* long preamble */
			FrameTime = (u16)(144+48+(FrameLength*8/(DataRate/10)));
		else
			/* short preamble */
			FrameTime = (u16)(72+24+(FrameLength*8/(DataRate/10)));

		if ((FrameLength*8 % (DataRate/10)) != 0) /* get the ceilling */
			FrameTime++;
	} else {	/* 802.11g DSSS-OFDM PLCP length field calculation. */
		N_DBPS = N_DBPSOfRate(DataRate);
		Ceiling = (16 + 8*FrameLength + 6) / N_DBPS
				+ (((16 + 8*FrameLength + 6) % N_DBPS) ? 1 : 0);
		FrameTime = (u16)(16 + 4 + 4*Ceiling + 6);
	}
	return FrameTime;
}

u16 N_DBPSOfRate(u16 DataRate)
{
	 u16 N_DBPS = 24;

	switch (DataRate) {
	case 60:
		N_DBPS = 24;
		break;
	case 90:
		N_DBPS = 36;
		break;
	case 120:
		N_DBPS = 48;
		break;
	case 180:
		N_DBPS = 72;
		break;
	case 240:
		N_DBPS = 96;
		break;
	case 360:
		N_DBPS = 144;
		break;
	case 480:
		N_DBPS = 192;
		break;
	case 540:
		N_DBPS = 216;
		break;
	default:
		break;
	}

	return N_DBPS;
}

//{by amy 080312
//
//	Description:
// 	For Netgear case, they want good-looking singal strength.
//		2004.12.05, by rcnjko.
//
long NetgearSignalStrengthTranslate(long LastSS, long CurrSS)
{
	long RetSS;

	// Step 1. Scale mapping.
	if (CurrSS >= 71 && CurrSS <= 100)
		RetSS = 90 + ((CurrSS - 70) / 3);
	else if (CurrSS >= 41 && CurrSS <= 70)
		RetSS = 78 + ((CurrSS - 40) / 3);
	else if (CurrSS >= 31 && CurrSS <= 40)
		RetSS = 66 + (CurrSS - 30);
	else if (CurrSS >= 21 && CurrSS <= 30)
		RetSS = 54 + (CurrSS - 20);
	else if (CurrSS >= 5 && CurrSS <= 20)
		RetSS = 42 + (((CurrSS - 5) * 2) / 3);
	else if (CurrSS == 4)
		RetSS = 36;
	else if (CurrSS == 3)
		RetSS = 27;
	else if (CurrSS == 2)
		RetSS = 18;
	else if (CurrSS == 1)
		RetSS = 9;
	else
		RetSS = CurrSS;

	// Step 2. Smoothing.
	if(LastSS > 0)
		RetSS = ((LastSS * 5) + (RetSS)+ 5) / 6;

	return RetSS;
}

//
//	Description:
//		Translate 0-100 signal strength index into dBm.
//
long TranslateToDbm8185(u8 SignalStrengthIndex)
{
	long SignalPower;

	// Translate to dBm (x=0.5y-95).
	SignalPower = (long)((SignalStrengthIndex + 1) >> 1);
	SignalPower -= 95;

	return SignalPower;
}

//
//	Description:
//		Perform signal smoothing for dynamic mechanism.
//		This is different with PerformSignalSmoothing8185 in smoothing fomula.
//		No dramatic adjustion is apply because dynamic mechanism need some degree
//		of correctness. Ported from 8187B.
//	2007-02-26, by Bruce.
//
void PerformUndecoratedSignalSmoothing8185(struct r8180_priv *priv,
					   bool bCckRate)
{
	// Determin the current packet is CCK rate.
	priv->bCurCCKPkt = bCckRate;

	if (priv->UndecoratedSmoothedSS >= 0)
		priv->UndecoratedSmoothedSS = ( (priv->UndecoratedSmoothedSS * 5) + (priv->SignalStrength * 10) ) / 6;
	else
		priv->UndecoratedSmoothedSS = priv->SignalStrength * 10;

	priv->UndercorateSmoothedRxPower = ( (priv->UndercorateSmoothedRxPower * 50) + (priv->RxPower* 11)) / 60;

	if (bCckRate)
		priv->CurCCKRSSI = priv->RSSI;
	else
		priv->CurCCKRSSI = 0;
}

//by amy 080312}

/* This is rough RX isr handling routine*/
void rtl8180_rx(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct sk_buff *tmp_skb;
	short first,last;
	u32 len;
	int lastlen;
	unsigned char quality, signal;
	u8 rate;
	u32 *tmp,*tmp2;
	u8 rx_desc_size;
	u8 padding;
	char rxpower = 0;
	u32 RXAGC = 0;
	long RxAGC_dBm = 0;
	u8	LNA=0, BB=0;
	u8 	LNA_gain[4]={02, 17, 29, 39};
	u8  Antenna = 0;
	struct ieee80211_hdr_4addr *hdr;
	u16 fc,type;
	u8 bHwError = 0,bCRC = 0,bICV = 0;
	bool	bCckRate = false;
	u8     RSSI = 0;
	long	SignalStrengthIndex = 0;
	struct ieee80211_rx_stats stats = {
		.signal = 0,
		.noise = -98,
		.rate = 0,
		.freq = IEEE80211_24GHZ_BAND,
	};

	stats.nic_type = NIC_8185B;
	rx_desc_size = 8;

	if ((*(priv->rxringtail)) & (1<<31)) {
		/* we have got an RX int, but the descriptor
		 * we are pointing is empty*/

		priv->stats.rxnodata++;
		priv->ieee80211->stats.rx_errors++;

		tmp2 = NULL;
		tmp = priv->rxringtail;
		do{
			if(tmp == priv->rxring)
				tmp  = priv->rxring + (priv->rxringcount - 1)*rx_desc_size;
			else
				tmp -= rx_desc_size;

			if(! (*tmp & (1<<31)))
				tmp2 = tmp;
		}while(tmp != priv->rxring);

		if(tmp2) priv->rxringtail = tmp2;
	}

	/* while there are filled descriptors */
	while(!(*(priv->rxringtail) & (1<<31))){
		if(*(priv->rxringtail) & (1<<26))
			DMESGW("RX buffer overflow");
		if(*(priv->rxringtail) & (1<<12))
			priv->stats.rxicverr++;

		if(*(priv->rxringtail) & (1<<27)){
			priv->stats.rxdmafail++;
			//DMESG("EE: RX DMA FAILED at buffer pointed by descriptor %x",(u32)priv->rxringtail);
			goto drop;
		}

		pci_dma_sync_single_for_cpu(priv->pdev,
				    priv->rxbuffer->dma,
				    priv->rxbuffersize * \
				    sizeof(u8),
				    PCI_DMA_FROMDEVICE);

		first = *(priv->rxringtail) & (1<<29) ? 1:0;
		if(first) priv->rx_prevlen=0;

		last = *(priv->rxringtail) & (1<<28) ? 1:0;
		if(last){
			lastlen=((*priv->rxringtail) &0xfff);

			/* if the last descriptor (that should
			 * tell us the total packet len) tell
			 * us something less than the descriptors
			 * len we had until now, then there is some
			 * problem..
			 * workaround to prevent kernel panic
			 */
			if(lastlen < priv->rx_prevlen)
				len=0;
			else
				len=lastlen-priv->rx_prevlen;

			if(*(priv->rxringtail) & (1<<13)) {
				if ((*(priv->rxringtail) & 0xfff) <500)
					priv->stats.rxcrcerrmin++;
				else if ((*(priv->rxringtail) & 0x0fff) >1000)
					priv->stats.rxcrcerrmax++;
				else
					priv->stats.rxcrcerrmid++;

			}

		}else{
			len = priv->rxbuffersize;
		}

		if(first && last) {
			padding = ((*(priv->rxringtail+3))&(0x04000000))>>26;
		}else if(first) {
			padding = ((*(priv->rxringtail+3))&(0x04000000))>>26;
			if(padding) {
				len -= 2;
			}
		}else {
			padding = 0;
		}
               padding = 0;
		priv->rx_prevlen+=len;

		if(priv->rx_prevlen > MAX_FRAG_THRESHOLD + 100){
			/* HW is probably passing several buggy frames
			* without FD or LD flag set.
			* Throw this garbage away to prevent skb
			* memory exausting
			*/
			if(!priv->rx_skb_complete)
				dev_kfree_skb_any(priv->rx_skb);
			priv->rx_skb_complete = 1;
		}

		signal=(unsigned char)(((*(priv->rxringtail+3))& (0x00ff0000))>>16);
		signal=(signal&0xfe)>>1;	// Modify by hikaru 6.6

		quality=(unsigned char)((*(priv->rxringtail+3)) & (0xff));

		stats.mac_time[0] = *(priv->rxringtail+1);
		stats.mac_time[1] = *(priv->rxringtail+2);
		rxpower =((char)(((*(priv->rxringtail+4))& (0x00ff0000))>>16))/2 - 42;
		RSSI = ((u8)(((*(priv->rxringtail+3)) & (0x0000ff00))>> 8)) & (0x7f);

		rate=((*(priv->rxringtail)) &
			((1<<23)|(1<<22)|(1<<21)|(1<<20)))>>20;

		stats.rate = rtl8180_rate2rate(rate);
		Antenna = (((*(priv->rxringtail +3))& (0x00008000)) == 0 )? 0:1 ;
//by amy for antenna
		if(!rtl8180_IsWirelessBMode(stats.rate))
		{ // OFDM rate.

			RxAGC_dBm = rxpower+1;	//bias
		}
		else
		{ // CCK rate.
			RxAGC_dBm = signal;//bit 0 discard

			LNA = (u8) (RxAGC_dBm & 0x60 ) >> 5 ; //bit 6~ bit 5
			BB  = (u8) (RxAGC_dBm & 0x1F);  // bit 4 ~ bit 0

   			RxAGC_dBm = -( LNA_gain[LNA] + (BB *2) ); //Pin_11b=-(LNA_gain+BB_gain) (dBm)

			RxAGC_dBm +=4; //bias
		}

		if(RxAGC_dBm & 0x80) //absolute value
   			RXAGC= ~(RxAGC_dBm)+1;
		bCckRate = rtl8180_IsWirelessBMode(stats.rate);
		// Translate RXAGC into 1-100.
		if(!rtl8180_IsWirelessBMode(stats.rate))
		{ // OFDM rate.
			if(RXAGC>90)
				RXAGC=90;
			else if(RXAGC<25)
				RXAGC=25;
			RXAGC=(90-RXAGC)*100/65;
		}
		else
		{ // CCK rate.
			if(RXAGC>95)
				RXAGC=95;
			else if(RXAGC<30)
				RXAGC=30;
			RXAGC=(95-RXAGC)*100/65;
		}
		priv->SignalStrength = (u8)RXAGC;
		priv->RecvSignalPower = RxAGC_dBm ;  // It can use directly by SD3 CMLin
		priv->RxPower = rxpower;
		priv->RSSI = RSSI;
//{by amy 080312
		// SQ translation formular is provided by SD3 DZ. 2006.06.27, by rcnjko.
		if(quality >= 127)
			quality = 1;//0; //0 will cause epc to show signal zero , walk aroud now;
		else if(quality < 27)
			quality = 100;
		else
			quality = 127 - quality;
		priv->SignalQuality = quality;
		if(!priv->card_8185)
			printk("check your card type\n");

		stats.signal = (u8)quality;//priv->wstats.qual.level = priv->SignalStrength;
		stats.signalstrength = RXAGC;
		if(stats.signalstrength > 100)
			stats.signalstrength = 100;
		stats.signalstrength = (stats.signalstrength * 70)/100 + 30;
	//	printk("==========================>rx : RXAGC is %d,signalstrength is %d\n",RXAGC,stats.signalstrength);
		stats.rssi = priv->wstats.qual.qual = priv->SignalQuality;
		stats.noise = priv->wstats.qual.noise = 100 - priv ->wstats.qual.qual;
//by amy 080312}
		bHwError = (((*(priv->rxringtail))& (0x00000fff)) == 4080)| (((*(priv->rxringtail))& (0x04000000)) != 0 )
			| (((*(priv->rxringtail))& (0x08000000)) != 0 )| (((~(*(priv->rxringtail)))& (0x10000000)) != 0 )| (((~(*(priv->rxringtail)))& (0x20000000)) != 0 );
		bCRC = ((*(priv->rxringtail)) & (0x00002000)) >> 13;
		bICV = ((*(priv->rxringtail)) & (0x00001000)) >> 12;
		hdr = (struct ieee80211_hdr_4addr *)priv->rxbuffer->buf;
		    fc = le16_to_cpu(hdr->frame_ctl);
	        type = WLAN_FC_GET_TYPE(fc);

			if((IEEE80211_FTYPE_CTL != type) &&
				(eqMacAddr(priv->ieee80211->current_network.bssid, (fc & IEEE80211_FCTL_TODS)? hdr->addr1 : (fc & IEEE80211_FCTL_FROMDS )? hdr->addr2 : hdr->addr3))
				 && (!bHwError) && (!bCRC)&& (!bICV))
			{
//by amy 080312
				// Perform signal smoothing for dynamic mechanism on demand.
				// This is different with PerformSignalSmoothing8185 in smoothing fomula.
				// No dramatic adjustion is apply because dynamic mechanism need some degree
				// of correctness. 2007.01.23, by shien chang.
				PerformUndecoratedSignalSmoothing8185(priv,bCckRate);
				//
				// For good-looking singal strength.
				//
				SignalStrengthIndex = NetgearSignalStrengthTranslate(
								priv->LastSignalStrengthInPercent,
								priv->SignalStrength);

				priv->LastSignalStrengthInPercent = SignalStrengthIndex;
				priv->Stats_SignalStrength = TranslateToDbm8185((u8)SignalStrengthIndex);
		//
		// We need more correct power of received packets and the  "SignalStrength" of RxStats is beautified,
		// so we record the correct power here.
		//
				priv->Stats_SignalQuality =(long) (priv->Stats_SignalQuality * 5 + (long)priv->SignalQuality + 5) / 6;
				priv->Stats_RecvSignalPower = (long)(priv->Stats_RecvSignalPower * 5 + priv->RecvSignalPower -1) / 6;

		// Figure out which antenna that received the lasted packet.
				priv->LastRxPktAntenna = Antenna ? 1 : 0; // 0: aux, 1: main.
//by amy 080312
			    SwAntennaDiversityRxOk8185(dev, priv->SignalStrength);
			}

//by amy for antenna
#ifndef DUMMY_RX
		if(first){
			if(!priv->rx_skb_complete){
				/* seems that HW sometimes fails to reiceve and
				   doesn't provide the last descriptor */
				dev_kfree_skb_any(priv->rx_skb);
				priv->stats.rxnolast++;
			}
			/* support for prism header has been originally added by Christian */
			if(priv->prism_hdr && priv->ieee80211->iw_mode == IW_MODE_MONITOR){

			}else{
				priv->rx_skb = dev_alloc_skb(len+2);
				if( !priv->rx_skb) goto drop;
			}

			priv->rx_skb_complete=0;
			priv->rx_skb->dev=dev;
		}else{
			/* if we are here we should  have already RXed
			* the first frame.
			* If we get here and the skb is not allocated then
			* we have just throw out garbage (skb not allocated)
			* and we are still rxing garbage....
			*/
			if(!priv->rx_skb_complete){

				tmp_skb= dev_alloc_skb(priv->rx_skb->len +len+2);

				if(!tmp_skb) goto drop;

				tmp_skb->dev=dev;

				memcpy(skb_put(tmp_skb,priv->rx_skb->len),
					priv->rx_skb->data,
					priv->rx_skb->len);

				dev_kfree_skb_any(priv->rx_skb);

				priv->rx_skb=tmp_skb;
			}
		}

		if(!priv->rx_skb_complete) {
			if(padding) {
				memcpy(skb_put(priv->rx_skb,len),
					(((unsigned char *)priv->rxbuffer->buf) + 2),len);
			} else {
				memcpy(skb_put(priv->rx_skb,len),
					priv->rxbuffer->buf,len);
			}
		}

		if(last && !priv->rx_skb_complete){
			if(priv->rx_skb->len > 4)
				skb_trim(priv->rx_skb,priv->rx_skb->len-4);
#ifndef RX_DONT_PASS_UL
			if(!ieee80211_rx(priv->ieee80211,
					 priv->rx_skb, &stats)){
#endif // RX_DONT_PASS_UL

				dev_kfree_skb_any(priv->rx_skb);
#ifndef RX_DONT_PASS_UL
			}
#endif
			priv->rx_skb_complete=1;
		}

#endif //DUMMY_RX
		pci_dma_sync_single_for_device(priv->pdev,
				    priv->rxbuffer->dma,
				    priv->rxbuffersize * \
				    sizeof(u8),
				    PCI_DMA_FROMDEVICE);

drop: // this is used when we have not enought mem
		/* restore the descriptor */
		*(priv->rxringtail+2)=priv->rxbuffer->dma;
		*(priv->rxringtail)=*(priv->rxringtail) &~ 0xfff;
		*(priv->rxringtail)=
			*(priv->rxringtail) | priv->rxbuffersize;

		*(priv->rxringtail)=
			*(priv->rxringtail) | (1<<31);

		priv->rxringtail+=rx_desc_size;
		if(priv->rxringtail >=
		   (priv->rxring)+(priv->rxringcount )*rx_desc_size)
			priv->rxringtail=priv->rxring;

		priv->rxbuffer=(priv->rxbuffer->next);
	}
}


void rtl8180_dma_kick(struct net_device *dev, int priority)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);
	write_nic_byte(dev, TX_DMA_POLLING,
			(1 << (priority + 1)) | priv->dma_poll_mask);
	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);

	force_pci_posting(dev);
}

void rtl8180_data_hard_stop(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);
	priv->dma_poll_stop_mask |= TPPOLLSTOP_AC_VIQ;
	write_nic_byte(dev,TPPollStop, priv->dma_poll_stop_mask);
	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);
}

void rtl8180_data_hard_resume(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);
	priv->dma_poll_stop_mask &= ~(TPPOLLSTOP_AC_VIQ);
	write_nic_byte(dev,TPPollStop, priv->dma_poll_stop_mask);
	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);
}

/* this function TX data frames when the ieee80211 stack requires this.
 * It checks also if we need to stop the ieee tx queue, eventually do it
 */
void rtl8180_hard_data_xmit(struct sk_buff *skb,struct net_device *dev, int
rate)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	int mode;
	struct ieee80211_hdr_3addr  *h = (struct ieee80211_hdr_3addr  *) skb->data;
	short morefrag = (h->frame_ctl) & IEEE80211_FCTL_MOREFRAGS;
	unsigned long flags;
	int priority;

	mode = priv->ieee80211->iw_mode;

	rate = ieeerate2rtlrate(rate);
	/*
	* This function doesn't require lock because we make
	* sure it's called with the tx_lock already acquired.
	* this come from the kernel's hard_xmit callback (trought
	* the ieee stack, or from the try_wake_queue (again trought
	* the ieee stack.
	*/
	priority = AC2Q(skb->priority);
	spin_lock_irqsave(&priv->tx_lock,flags);

	if(priv->ieee80211->bHwRadioOff)
	{
		spin_unlock_irqrestore(&priv->tx_lock,flags);

		return;
	}

	if (!check_nic_enought_desc(dev, priority)){
		DMESGW("Error: no descriptor left by previous TX (avail %d) ",
			get_curr_tx_free_desc(dev, priority));
		ieee80211_stop_queue(priv->ieee80211);
	}
	rtl8180_tx(dev, skb->data, skb->len, priority, morefrag,0,rate);
	if (!check_nic_enought_desc(dev, priority))
		ieee80211_stop_queue(priv->ieee80211);

	spin_unlock_irqrestore(&priv->tx_lock,flags);
}

/* This is a rough attempt to TX a frame
 * This is called by the ieee 80211 stack to TX management frames.
 * If the ring is full packet are dropped (for data frame the queue
 * is stopped before this can happen). For this reason it is better
 * if the descriptors are larger than the largest management frame
 * we intend to TX: i'm unsure what the HW does if it will not found
 * the last fragment of a frame because it has been dropped...
 * Since queues for Management and Data frames are different we
 * might use a different lock than tx_lock (for example mgmt_tx_lock)
 */
/* these function may loops if invoked with 0 descriptors or 0 len buffer*/
int rtl8180_hard_start_xmit(struct sk_buff *skb,struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	unsigned long flags;
	int priority;

	priority = MANAGE_PRIORITY;

	spin_lock_irqsave(&priv->tx_lock,flags);

	if (priv->ieee80211->bHwRadioOff) {
		spin_unlock_irqrestore(&priv->tx_lock,flags);
		dev_kfree_skb_any(skb);
		return NETDEV_TX_OK;
	}

	rtl8180_tx(dev, skb->data, skb->len, priority,
		0, 0,ieeerate2rtlrate(priv->ieee80211->basic_rate));

	priv->ieee80211->stats.tx_bytes+=skb->len;
	priv->ieee80211->stats.tx_packets++;
	spin_unlock_irqrestore(&priv->tx_lock,flags);

	dev_kfree_skb_any(skb);
	return NETDEV_TX_OK;
}

// longpre 144+48 shortpre 72+24
u16 rtl8180_len2duration(u32 len, short rate,short* ext)
{
	u16 duration;
	u16 drift;
	*ext=0;

	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;
}

void rtl8180_prepare_beacon(struct net_device *dev)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct sk_buff *skb;

	u16 word  = read_nic_word(dev, BcnItv);
	word &= ~BcnItv_BcnItv; // clear Bcn_Itv
	word |= cpu_to_le16(priv->ieee80211->current_network.beacon_interval);//0x64;
	write_nic_word(dev, BcnItv, word);

	skb = ieee80211_get_beacon(priv->ieee80211);
	if(skb){
		rtl8180_tx(dev,skb->data,skb->len,BEACON_PRIORITY,
			0,0,ieeerate2rtlrate(priv->ieee80211->basic_rate));
		dev_kfree_skb_any(skb);
	}
}

/* This function do the real dirty work: it enqueues a TX command
 * descriptor in the ring buffer, copyes the frame in a TX buffer
 * and kicks the NIC to ensure it does the DMA transfer.
 */
short rtl8180_tx(struct net_device *dev, u8* txbuf, int len, int priority,
		 short morefrag, short descfrag, int rate)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u32 *tail,*temp_tail;
	u32 *begin;
	u32 *buf;
	int i;
	int remain;
	int buflen;
	int count;
	u16 duration;
	short ext;
	struct buffer* buflist;
	struct ieee80211_hdr_3addr *frag_hdr = (struct ieee80211_hdr_3addr *)txbuf;
	u8 dest[ETH_ALEN];
	u8			bUseShortPreamble = 0;
	u8			bCTSEnable = 0;
	u8			bRTSEnable = 0;
	u16 			Duration = 0;
	u16			RtsDur = 0;
	u16			ThisFrameTime = 0;
	u16			TxDescDuration = 0;
	u8 			ownbit_flag = false; //added by david woo for sync Tx, 2007.12.14

	switch(priority) {
	case MANAGE_PRIORITY:
		tail=priv->txmapringtail;
		begin=priv->txmapring;
		buflist = priv->txmapbufstail;
		count = priv->txringcount;
		break;
	case BK_PRIORITY:
		tail=priv->txbkpringtail;
		begin=priv->txbkpring;
		buflist = priv->txbkpbufstail;
		count = priv->txringcount;
		break;
	case BE_PRIORITY:
		tail=priv->txbepringtail;
		begin=priv->txbepring;
		buflist = priv->txbepbufstail;
		count = priv->txringcount;
		break;
	case VI_PRIORITY:
		tail=priv->txvipringtail;
		begin=priv->txvipring;
		buflist = priv->txvipbufstail;
		count = priv->txringcount;
		break;
	case VO_PRIORITY:
		tail=priv->txvopringtail;
		begin=priv->txvopring;
		buflist = priv->txvopbufstail;
		count = priv->txringcount;
		break;
	case HI_PRIORITY:
		tail=priv->txhpringtail;
		begin=priv->txhpring;
		buflist = priv->txhpbufstail;
		count = priv->txringcount;
		break;
	case BEACON_PRIORITY:
		tail=priv->txbeaconringtail;
		begin=priv->txbeaconring;
		buflist = priv->txbeaconbufstail;
		count = priv->txbeaconcount;
		break;
	default:
		return -1;
		break;
 	}

		memcpy(&dest, frag_hdr->addr1, ETH_ALEN);
		if (is_multicast_ether_addr(dest) ||
				is_broadcast_ether_addr(dest))
		{
			Duration = 0;
			RtsDur = 0;
			bRTSEnable = 0;
			bCTSEnable = 0;

			ThisFrameTime = ComputeTxTime(len + sCrcLng, rtl8180_rate2rate(rate), 0, bUseShortPreamble);
			TxDescDuration = ThisFrameTime;
		} else {// Unicast packet
			u16 AckTime;

			//YJ,add,080828,for Keep alive
			priv->NumTxUnicast++;

			// Figure out ACK rate according to BSS basic rate and Tx rate, 2006.03.08 by rcnjko.
			AckTime = ComputeTxTime(14, 10,0, 0);	// AckCTSLng = 14 use 1M bps send

			if ( ((len + sCrcLng) > priv->rts) && priv->rts )
			{ // RTS/CTS.
				u16 RtsTime, CtsTime;
				//u16 CtsRate;
				bRTSEnable = 1;
				bCTSEnable = 0;

				// Rate and time required for RTS.
				RtsTime = ComputeTxTime( sAckCtsLng/8,priv->ieee80211->basic_rate, 0, 0);
				// Rate and time required for CTS.
				CtsTime = ComputeTxTime(14, 10,0, 0);	// AckCTSLng = 14 use 1M bps send

				// Figure out time required to transmit this frame.
				ThisFrameTime = ComputeTxTime(len + sCrcLng,
						rtl8180_rate2rate(rate),
						0,
						bUseShortPreamble);

				// RTS-CTS-ThisFrame-ACK.
				RtsDur = CtsTime + ThisFrameTime + AckTime + 3*aSifsTime;

				TxDescDuration = RtsTime + RtsDur;
			}
			else {// Normal case.
				bCTSEnable = 0;
				bRTSEnable = 0;
				RtsDur = 0;

				ThisFrameTime = ComputeTxTime(len + sCrcLng, rtl8180_rate2rate(rate), 0, bUseShortPreamble);
				TxDescDuration = ThisFrameTime + aSifsTime + AckTime;
			}

			if(!(frag_hdr->frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { //no more fragment
				// ThisFrame-ACK.
				Duration = aSifsTime + AckTime;
			} else { // One or more fragments remained.
				u16 NextFragTime;
				NextFragTime = ComputeTxTime( len + sCrcLng, //pretend following packet length equal current packet
						rtl8180_rate2rate(rate),
						0,
						bUseShortPreamble );

				//ThisFrag-ACk-NextFrag-ACK.
				Duration = NextFragTime + 3*aSifsTime + 2*AckTime;
			}

		} // End of Unicast packet

		frag_hdr->duration_id = Duration;

	buflen=priv->txbuffsize;
	remain=len;
	temp_tail = tail;

	while(remain!=0){
		mb();
		if(!buflist){
			DMESGE("TX buffer error, cannot TX frames. pri %d.", priority);
			return -1;
		}
		buf=buflist->buf;

		if ((*tail & (1 << 31)) && (priority != BEACON_PRIORITY)) {
			DMESGW("No more TX desc, returning %x of %x",
			       remain, len);
			priv->stats.txrdu++;
			return remain;
		}

		*tail= 0; // zeroes header
		*(tail+1) = 0;
		*(tail+3) = 0;
		*(tail+5) = 0;
		*(tail+6) = 0;
		*(tail+7) = 0;

		if(priv->card_8185){
			//FIXME: this should be triggered by HW encryption parameters.
			*tail |= (1<<15); //no encrypt
		}

		if(remain==len && !descfrag) {
			ownbit_flag = false;	//added by david woo,2007.12.14
			*tail = *tail| (1<<29) ; //fist segment of the packet
			*tail = *tail |(len);
		} else {
			ownbit_flag = true;
		}

		for(i=0;i<buflen&& remain >0;i++,remain--){
			((u8*)buf)[i]=txbuf[i]; //copy data into descriptor pointed DMAble buffer
			if(remain == 4 && i+4 >= buflen) break;
			/* ensure the last desc has at least 4 bytes payload */

		}
		txbuf = txbuf + i;
		*(tail+3)=*(tail+3) &~ 0xfff;
		*(tail+3)=*(tail+3) | i; // buffer lenght
		// Use short preamble or not
		if (priv->ieee80211->current_network.capability&WLAN_CAPABILITY_SHORT_PREAMBLE)
			if (priv->plcp_preamble_mode==1 && rate!=0)	//  short mode now, not long!
			;//	*tail |= (1<<16);				// enable short preamble mode.

		if(bCTSEnable) {
			*tail |= (1<<18);
		}

		if(bRTSEnable) //rts enable
		{
			*tail |= ((ieeerate2rtlrate(priv->ieee80211->basic_rate))<<19);//RTS RATE
			*tail |= (1<<23);//rts enable
			*(tail+1) |=(RtsDur&0xffff);//RTS Duration
		}
		*(tail+3) |= ((TxDescDuration&0xffff)<<16); //DURATION
//	        *(tail+3) |= (0xe6<<16);
        	*(tail+5) |= (11<<8);//(priv->retry_data<<8); //retry lim ;

		*tail = *tail | ((rate&0xf) << 24);

		/* hw_plcp_len is not used for rtl8180 chip */
		/* FIXME */
		if(priv->card_8185 == 0 || !priv->hw_plcp_len){
			duration = rtl8180_len2duration(len, rate, &ext);
			*(tail+1) = *(tail+1) | ((duration & 0x7fff)<<16);
			if(ext) *(tail+1) = *(tail+1) |(1<<31); //plcp length extension
		}

		if(morefrag) *tail = (*tail) | (1<<17); // more fragment
		if(!remain) *tail = (*tail) | (1<<28); // last segment of frame

               *(tail+5) = *(tail+5)|(2<<27);
                *(tail+7) = *(tail+7)|(1<<4);

		wmb();
		if(ownbit_flag)
		{
			*tail = *tail | (1<<31); // descriptor ready to be txed
		}

		if((tail - begin)/8 == count-1)
			tail=begin;
		else
			tail=tail+8;

		buflist=buflist->next;

		mb();

		switch(priority) {
			case MANAGE_PRIORITY:
				priv->txmapringtail=tail;
				priv->txmapbufstail=buflist;
				break;
			case BK_PRIORITY:
				priv->txbkpringtail=tail;
				priv->txbkpbufstail=buflist;
				break;
			case BE_PRIORITY:
				priv->txbepringtail=tail;
				priv->txbepbufstail=buflist;
				break;
			case VI_PRIORITY:
				priv->txvipringtail=tail;
				priv->txvipbufstail=buflist;
				break;
			case VO_PRIORITY:
				priv->txvopringtail=tail;
				priv->txvopbufstail=buflist;
				break;
			case HI_PRIORITY:
				priv->txhpringtail=tail;
				priv->txhpbufstail = buflist;
				break;
			case BEACON_PRIORITY:
				/* the HW seems to be happy with the 1st
				 * descriptor filled and the 2nd empty...
				 * So always update descriptor 1 and never
				 * touch 2nd
				 */
				break;
		}
	}
	*temp_tail = *temp_tail | (1<<31); // descriptor ready to be txed
	rtl8180_dma_kick(dev,priority);

	return 0;
}

void rtl8180_irq_rx_tasklet(struct r8180_priv * priv);

void rtl8180_link_change(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u16 beacon_interval;
	struct ieee80211_network *net = &priv->ieee80211->current_network;

	rtl8180_update_msr(dev);

	rtl8180_set_mode(dev,EPROM_CMD_CONFIG);

	write_nic_dword(dev,BSSID,((u32*)net->bssid)[0]);
	write_nic_word(dev,BSSID+4,((u16*)net->bssid)[2]);

	beacon_interval  = read_nic_dword(dev,BEACON_INTERVAL);
	beacon_interval &= ~ BEACON_INTERVAL_MASK;
	beacon_interval |= net->beacon_interval;
	write_nic_dword(dev, BEACON_INTERVAL, beacon_interval);

	rtl8180_set_mode(dev, EPROM_CMD_NORMAL);

	if(priv->card_8185)
		rtl8180_set_chan(dev, priv->chan);
}

void rtl8180_rq_tx_ack(struct net_device *dev){

	struct r8180_priv *priv = ieee80211_priv(dev);

	write_nic_byte(dev,CONFIG4,read_nic_byte(dev,CONFIG4)|CONFIG4_PWRMGT);
	priv->ack_tx_to_ieee = 1;
}

short rtl8180_is_tx_queue_empty(struct net_device *dev){

	struct r8180_priv *priv = ieee80211_priv(dev);
	u32* d;

	for (d = priv->txmapring;
		d < priv->txmapring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;

	for (d = priv->txbkpring;
		d < priv->txbkpring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;

	for (d = priv->txbepring;
		d < priv->txbepring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;

	for (d = priv->txvipring;
		d < priv->txvipring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;

	for (d = priv->txvopring;
		d < priv->txvopring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;

	for (d = priv->txhpring;
		d < priv->txhpring + priv->txringcount;d+=8)
			if(*d & (1<<31)) return 0;
	return 1;
}
/* FIXME FIXME 5msecs is random */
#define HW_WAKE_DELAY 5

void rtl8180_hw_wakeup(struct net_device *dev)
{
	unsigned long flags;
	struct r8180_priv *priv = ieee80211_priv(dev);

	spin_lock_irqsave(&priv->ps_lock,flags);
	write_nic_byte(dev,CONFIG4,read_nic_byte(dev,CONFIG4)&~CONFIG4_PWRMGT);
	if (priv->rf_wakeup)
		priv->rf_wakeup(dev);
	spin_unlock_irqrestore(&priv->ps_lock,flags);
}

void rtl8180_hw_sleep_down(struct net_device *dev)
{
        unsigned long flags;
        struct r8180_priv *priv = ieee80211_priv(dev);

        spin_lock_irqsave(&priv->ps_lock,flags);
        if(priv->rf_sleep)
                priv->rf_sleep(dev);
        spin_unlock_irqrestore(&priv->ps_lock,flags);
}

void rtl8180_hw_sleep(struct net_device *dev, u32 th, u32 tl)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u32 rb = jiffies;
	unsigned long flags;

	spin_lock_irqsave(&priv->ps_lock,flags);

	/* Writing HW register with 0 equals to disable
	 * the timer, that is not really what we want
	 */
	tl -= MSECS(4+16+7);

	/* If the interval in witch we are requested to sleep is too
	 * short then give up and remain awake
	 */
	if(((tl>=rb)&& (tl-rb) <= MSECS(MIN_SLEEP_TIME))
		||((rb>tl)&& (rb-tl) < MSECS(MIN_SLEEP_TIME))) {
		spin_unlock_irqrestore(&priv->ps_lock,flags);
		printk("too short to sleep\n");
		return;
	}

	{
		u32 tmp = (tl>rb)?(tl-rb):(rb-tl);

		priv->DozePeriodInPast2Sec += jiffies_to_msecs(tmp);

		queue_delayed_work(priv->ieee80211->wq, &priv->ieee80211->hw_wakeup_wq, tmp); //as tl may be less than rb
	}
	/* if we suspect the TimerInt is gone beyond tl
	 * while setting it, then give up
	 */

	if(((tl > rb) && ((tl-rb) > MSECS(MAX_SLEEP_TIME)))||
		((tl < rb) && ((rb-tl) > MSECS(MAX_SLEEP_TIME)))) {
		spin_unlock_irqrestore(&priv->ps_lock,flags);
		return;
	}

	queue_work(priv->ieee80211->wq, (void *)&priv->ieee80211->hw_sleep_wq);
	spin_unlock_irqrestore(&priv->ps_lock,flags);
}

void rtl8180_wmm_param_update(struct work_struct * work)
{
	struct ieee80211_device * ieee = container_of(work, struct ieee80211_device,wmm_param_update_wq);
	struct net_device *dev = ieee->dev;
	u8 *ac_param = (u8 *)(ieee->current_network.wmm_param);
	u8 mode = ieee->current_network.mode;
	AC_CODING	eACI;
	AC_PARAM	AcParam;
	PAC_PARAM	pAcParam;
	u8 i;

	if(!ieee->current_network.QoS_Enable){
		//legacy ac_xx_param update
		AcParam.longData = 0;
		AcParam.f.AciAifsn.f.AIFSN = 2; // Follow 802.11 DIFS.
		AcParam.f.AciAifsn.f.ACM = 0;
		AcParam.f.Ecw.f.ECWmin = 3; // Follow 802.11 CWmin.
		AcParam.f.Ecw.f.ECWmax = 7; // Follow 802.11 CWmax.
		AcParam.f.TXOPLimit = 0;
		for(eACI = 0; eACI < AC_MAX; eACI++){
			AcParam.f.AciAifsn.f.ACI = (u8)eACI;
			{
				u8		u1bAIFS;
				u32		u4bAcParam;
				pAcParam = (PAC_PARAM)(&AcParam);
				// Retrive paramters to udpate.
				u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN *(((mode&IEEE_G) == IEEE_G)?9:20) + aSifsTime;
				u4bAcParam = ((((u32)(pAcParam->f.TXOPLimit))<<AC_PARAM_TXOP_LIMIT_OFFSET)|
					      (((u32)(pAcParam->f.Ecw.f.ECWmax))<<AC_PARAM_ECW_MAX_OFFSET)|
					      (((u32)(pAcParam->f.Ecw.f.ECWmin))<<AC_PARAM_ECW_MIN_OFFSET)|
					       (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET));
				switch(eACI){
					case AC1_BK:
						write_nic_dword(dev, AC_BK_PARAM, u4bAcParam);
						break;
					case AC0_BE:
						write_nic_dword(dev, AC_BE_PARAM, u4bAcParam);
						break;
					case AC2_VI:
						write_nic_dword(dev, AC_VI_PARAM, u4bAcParam);
						break;
					case AC3_VO:
						write_nic_dword(dev, AC_VO_PARAM, u4bAcParam);
						break;
					default:
						printk(KERN_WARNING "SetHwReg8185():invalid ACI: %d!\n", eACI);
						break;
				}
			}
		}
		return;
	}

	for(i = 0; i < AC_MAX; i++){
		//AcParam.longData = 0;
		pAcParam = (AC_PARAM * )ac_param;
		{
			AC_CODING	eACI;
			u8		u1bAIFS;
			u32		u4bAcParam;

			// Retrive paramters to udpate.
			eACI = pAcParam->f.AciAifsn.f.ACI;
			//Mode G/A: slotTimeTimer = 9; Mode B: 20
			u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * (((mode&IEEE_G) == IEEE_G)?9:20) + aSifsTime;
			u4bAcParam = (	(((u32)(pAcParam->f.TXOPLimit)) << AC_PARAM_TXOP_LIMIT_OFFSET)	|
					(((u32)(pAcParam->f.Ecw.f.ECWmax)) << AC_PARAM_ECW_MAX_OFFSET)	|
					(((u32)(pAcParam->f.Ecw.f.ECWmin)) << AC_PARAM_ECW_MIN_OFFSET)	|
					(((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET));

			switch(eACI){
				case AC1_BK:
					write_nic_dword(dev, AC_BK_PARAM, u4bAcParam);
					break;
				case AC0_BE:
					write_nic_dword(dev, AC_BE_PARAM, u4bAcParam);
					break;
				case AC2_VI:
					write_nic_dword(dev, AC_VI_PARAM, u4bAcParam);
					break;
				case AC3_VO:
					write_nic_dword(dev, AC_VO_PARAM, u4bAcParam);
					break;
				default:
					printk(KERN_WARNING "SetHwReg8185(): invalid ACI: %d !\n", eACI);
					break;
			}
		}
		ac_param += (sizeof(AC_PARAM));
	}
}

void rtl8180_tx_irq_wq(struct work_struct *work);
void rtl8180_restart_wq(struct work_struct *work);
//void rtl8180_rq_tx_ack(struct work_struct *work);
void rtl8180_watch_dog_wq(struct work_struct *work);
void rtl8180_hw_wakeup_wq(struct work_struct *work);
void rtl8180_hw_sleep_wq(struct work_struct *work);
void rtl8180_sw_antenna_wq(struct work_struct *work);
void rtl8180_watch_dog(struct net_device *dev);

void watch_dog_adaptive(unsigned long data)
{
	struct r8180_priv* priv = ieee80211_priv((struct net_device *)data);

	if (!priv->up) {
		DMESG("<----watch_dog_adaptive():driver is not up!\n");
		return;
	}

	// Tx High Power Mechanism.
#ifdef HIGH_POWER
	if(CheckHighPower((struct net_device *)data))
	{
		queue_work(priv->ieee80211->wq, (void *)&priv->ieee80211->tx_pw_wq);
	}
#endif

	// Tx Power Tracking on 87SE.
#ifdef TX_TRACK
	//if( priv->bTxPowerTrack )	//lzm mod 080826
	if( CheckTxPwrTracking((struct net_device *)data));
		TxPwrTracking87SE((struct net_device *)data);
#endif

	// Perform DIG immediately.
#ifdef SW_DIG
	if(CheckDig((struct net_device *)data) == true)
	{
		queue_work(priv->ieee80211->wq, (void *)&priv->ieee80211->hw_dig_wq);
	}
#endif
   	rtl8180_watch_dog((struct net_device *)data);

	queue_work(priv->ieee80211->wq, (void *)&priv->ieee80211->GPIOChangeRFWorkItem);

   	priv->watch_dog_timer.expires = jiffies + MSECS(IEEE80211_WATCH_DOG_TIME);
	add_timer(&priv->watch_dog_timer);
}

static CHANNEL_LIST ChannelPlan[] = {
	{{1,2,3,4,5,6,7,8,9,10,11,36,40,44,48,52,56,60,64},19},  		//FCC
	{{1,2,3,4,5,6,7,8,9,10,11},11},                    				//IC
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21},  	//ETSI
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21},    //Spain. Change to ETSI.
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21},  	//France. Change to ETSI.
	{{14,36,40,44,48,52,56,60,64},9},						//MKK
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,14, 36,40,44,48,52,56,60,64},22},//MKK1
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21},	//Israel.
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,34,38,42,46},17},			// For 11a , TELEC
	{{1,2,3,4,5,6,7,8,9,10,11,12,13,14},14},  //For Global Domain. 1-11:active scan, 12-14 passive scan. //+YJ, 080626
	{{1,2,3,4,5,6,7,8,9,10,11,12,13},13} //world wide 13: ch1~ch11 active scan, ch12~13 passive //lzm add 080826
};

static void rtl8180_set_channel_map(u8 channel_plan, struct ieee80211_device *ieee)
{
	int i;

	//lzm add 080826
	ieee->MinPassiveChnlNum=MAX_CHANNEL_NUMBER+1;
	ieee->IbssStartChnl=0;

	switch (channel_plan)
	{
		case COUNTRY_CODE_FCC:
		case COUNTRY_CODE_IC:
		case COUNTRY_CODE_ETSI:
		case COUNTRY_CODE_SPAIN:
		case COUNTRY_CODE_FRANCE:
		case COUNTRY_CODE_MKK:
		case COUNTRY_CODE_MKK1:
		case COUNTRY_CODE_ISRAEL:
		case COUNTRY_CODE_TELEC:
		{
			Dot11d_Init(ieee);
			ieee->bGlobalDomain = false;
			if (ChannelPlan[channel_plan].Len != 0){
				// Clear old channel map
				memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map));
				// Set new channel map
				for (i=0;i<ChannelPlan[channel_plan].Len;i++)
				{
					if(ChannelPlan[channel_plan].Channel[i] <= 14)
						GET_DOT11D_INFO(ieee)->channel_map[ChannelPlan[channel_plan].Channel[i]] = 1;
				}
			}
			break;
		}
		case COUNTRY_CODE_GLOBAL_DOMAIN:
		{
			GET_DOT11D_INFO(ieee)->bEnabled = 0;
			Dot11d_Reset(ieee);
			ieee->bGlobalDomain = true;
			break;
		}
		case COUNTRY_CODE_WORLD_WIDE_13_INDEX://lzm add 080826
		{
		ieee->MinPassiveChnlNum=12;
		ieee->IbssStartChnl= 10;
		break;
		}
		default:
		{
			Dot11d_Init(ieee);
			ieee->bGlobalDomain = false;
			memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map));
			for (i=1;i<=14;i++)
			{
				GET_DOT11D_INFO(ieee)->channel_map[i] = 1;
			}
			break;
		}
	}
}

void GPIOChangeRFWorkItemCallBack(struct work_struct *work);

//YJ,add,080828
static void rtl8180_statistics_init(struct Stats *pstats)
{
	memset(pstats, 0, sizeof(struct Stats));
}

static void rtl8180_link_detect_init(plink_detect_t plink_detect)
{
	memset(plink_detect, 0, sizeof(link_detect_t));
	plink_detect->SlotNum = DEFAULT_SLOT_NUM;
}
//YJ,add,080828,end

short rtl8180_init(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u16 word;
	u16 version;
	u8 hw_version;
	//u8 config3;
	u32 usValue;
	u16 tmpu16;
	int i, j;

	priv->channel_plan = eprom_read(dev, EEPROM_COUNTRY_CODE>>1) & 0xFF;
	if(priv->channel_plan > COUNTRY_CODE_GLOBAL_DOMAIN){
		printk("rtl8180_init:Error channel plan! Set to default.\n");
		priv->channel_plan = 0;
	}

	DMESG("Channel plan is %d\n",priv->channel_plan);
	rtl8180_set_channel_map(priv->channel_plan, priv->ieee80211);

	//FIXME: these constants are placed in a bad pleace.
	priv->txbuffsize = 2048;//1024;
	priv->txringcount = 32;//32;
	priv->rxbuffersize = 2048;//1024;
	priv->rxringcount = 64;//32;
	priv->txbeaconcount = 2;
	priv->rx_skb_complete = 1;

	priv->RegThreeWireMode = HW_THREE_WIRE_SI;

	priv->RFChangeInProgress = false;
	priv->SetRFPowerStateInProgress = false;
	priv->RFProgType = 0;
	priv->bInHctTest = false;

	priv->irq_enabled=0;

	rtl8180_statistics_init(&priv->stats);
	rtl8180_link_detect_init(&priv->link_detect);

	priv->ack_tx_to_ieee = 0;
	priv->ieee80211->current_network.beacon_interval = DEFAULT_BEACONINTERVAL;
	priv->ieee80211->iw_mode = IW_MODE_INFRA;
	priv->ieee80211->softmac_features  = IEEE_SOFTMAC_SCAN |
		IEEE_SOFTMAC_ASSOCIATE | IEEE_SOFTMAC_PROBERQ |
		IEEE_SOFTMAC_PROBERS | IEEE_SOFTMAC_TX_QUEUE;
	priv->ieee80211->active_scan = 1;
	priv->ieee80211->rate = 110; //11 mbps
	priv->ieee80211->modulation = IEEE80211_CCK_MODULATION;
	priv->ieee80211->host_encrypt = 1;
	priv->ieee80211->host_decrypt = 1;
	priv->ieee80211->sta_wake_up = rtl8180_hw_wakeup;
	priv->ieee80211->ps_request_tx_ack = rtl8180_rq_tx_ack;
	priv->ieee80211->enter_sleep_state = rtl8180_hw_sleep;
	priv->ieee80211->ps_is_queue_empty = rtl8180_is_tx_queue_empty;

	priv->hw_wep = hwwep;
	priv->prism_hdr=0;
	priv->dev=dev;
	priv->retry_rts = DEFAULT_RETRY_RTS;
	priv->retry_data = DEFAULT_RETRY_DATA;
	priv->RFChangeInProgress = false;
	priv->SetRFPowerStateInProgress = false;
	priv->RFProgType = 0;
	priv->bInHctTest = false;
	priv->bInactivePs = true;//false;
	priv->ieee80211->bInactivePs = priv->bInactivePs;
	priv->bSwRfProcessing = false;
	priv->eRFPowerState = eRfOff;
	priv->RfOffReason = 0;
	priv->LedStrategy = SW_LED_MODE0;
	priv->TxPollingTimes = 0;//lzm add 080826
	priv->bLeisurePs = true;
	priv->dot11PowerSaveMode = eActive;
//by amy for antenna
	priv->AdMinCheckPeriod = 5;
	priv->AdMaxCheckPeriod = 10;
// Lower signal strength threshold to fit the HW participation in antenna diversity. +by amy 080312
	priv->AdMaxRxSsThreshold = 30;//60->30
	priv->AdRxSsThreshold = 20;//50->20
	priv->AdCheckPeriod = priv->AdMinCheckPeriod;
	priv->AdTickCount = 0;
	priv->AdRxSignalStrength = -1;
	priv->RegSwAntennaDiversityMechanism = 0;
	priv->RegDefaultAntenna = 0;
	priv->SignalStrength = 0;
	priv->AdRxOkCnt = 0;
	priv->CurrAntennaIndex = 0;
	priv->AdRxSsBeforeSwitched = 0;
	init_timer(&priv->SwAntennaDiversityTimer);
	priv->SwAntennaDiversityTimer.data = (unsigned long)dev;
	priv->SwAntennaDiversityTimer.function = (void *)SwAntennaDiversityTimerCallback;
//by amy for antenna
//{by amy 080312
	priv->bDigMechanism = 1;
	priv->InitialGain = 6;
	priv->bXtalCalibration = false;
	priv->XtalCal_Xin = 0;
	priv->XtalCal_Xout = 0;
	priv->bTxPowerTrack = false;
	priv->ThermalMeter = 0;
	priv->FalseAlarmRegValue = 0;
	priv->RegDigOfdmFaUpTh = 0xc; // Upper threhold of OFDM false alarm, which is used in DIG.
	priv->DIG_NumberFallbackVote = 0;
	priv->DIG_NumberUpgradeVote = 0;
	priv->LastSignalStrengthInPercent = 0;
	priv->Stats_SignalStrength = 0;
	priv->LastRxPktAntenna = 0;
	priv->SignalQuality = 0; // in 0-100 index.
	priv->Stats_SignalQuality = 0;
	priv->RecvSignalPower = 0; // in dBm.
	priv->Stats_RecvSignalPower = 0;
	priv->AdMainAntennaRxOkCnt = 0;
	priv->AdAuxAntennaRxOkCnt = 0;
	priv->bHWAdSwitched = false;
	priv->bRegHighPowerMechanism = true;
	priv->RegHiPwrUpperTh = 77;
	priv->RegHiPwrLowerTh = 75;
	priv->RegRSSIHiPwrUpperTh = 70;
	priv->RegRSSIHiPwrLowerTh = 20;
	priv->bCurCCKPkt = false;
	priv->UndecoratedSmoothedSS = -1;
	priv->bToUpdateTxPwr = false;
	priv->CurCCKRSSI = 0;
	priv->RxPower = 0;
	priv->RSSI = 0;
	priv->NumTxOkTotal = 0;
	priv->NumTxUnicast = 0;
	priv->keepAliveLevel = DEFAULT_KEEP_ALIVE_LEVEL;
	priv->PowerProfile = POWER_PROFILE_AC;
    priv->CurrRetryCnt=0;
    priv->LastRetryCnt=0;
    priv->LastTxokCnt=0;
    priv->LastRxokCnt=0;
    priv->LastRetryRate=0;
    priv->bTryuping=0;
    priv->CurrTxRate=0;
    priv->CurrRetryRate=0;
    priv->TryupingCount=0;
    priv->TryupingCountNoData=0;
    priv->TryDownCountLowData=0;
    priv->LastTxOKBytes=0;
    priv->LastFailTxRate=0;
    priv->LastFailTxRateSS=0;
    priv->FailTxRateCount=0;
    priv->LastTxThroughput=0;
    priv->NumTxOkBytesTotal=0;
	priv->ForcedDataRate = 0;
	priv->RegBModeGainStage = 1;

	priv->promisc = (dev->flags & IFF_PROMISC) ? 1:0;
	spin_lock_init(&priv->irq_lock);
	spin_lock_init(&priv->irq_th_lock);
	spin_lock_init(&priv->tx_lock);
	spin_lock_init(&priv->ps_lock);
	spin_lock_init(&priv->rf_ps_lock);
	sema_init(&priv->wx_sem,1);
	sema_init(&priv->rf_state,1);
	INIT_WORK(&priv->reset_wq,(void*) rtl8180_restart_wq);
	INIT_WORK(&priv->tx_irq_wq,(void*) rtl8180_tx_irq_wq);
	INIT_DELAYED_WORK(&priv->ieee80211->hw_wakeup_wq,(void*) rtl8180_hw_wakeup_wq);
	INIT_DELAYED_WORK(&priv->ieee80211->hw_sleep_wq,(void*) rtl8180_hw_sleep_wq);
	INIT_WORK(&priv->ieee80211->wmm_param_update_wq,(void*) rtl8180_wmm_param_update);
	INIT_DELAYED_WORK(&priv->ieee80211->rate_adapter_wq,(void*)rtl8180_rate_adapter);//+by amy 080312
	INIT_DELAYED_WORK(&priv->ieee80211->hw_dig_wq,(void*)rtl8180_hw_dig_wq);//+by amy 080312
	INIT_DELAYED_WORK(&priv->ieee80211->tx_pw_wq,(void*)rtl8180_tx_pw_wq);//+by amy 080312

	INIT_DELAYED_WORK(&priv->ieee80211->GPIOChangeRFWorkItem,(void*) GPIOChangeRFWorkItemCallBack);

	tasklet_init(&priv->irq_rx_tasklet,
		     (void(*)(unsigned long)) rtl8180_irq_rx_tasklet,
		     (unsigned long)priv);

    init_timer(&priv->watch_dog_timer);
	priv->watch_dog_timer.data = (unsigned long)dev;
	priv->watch_dog_timer.function = watch_dog_adaptive;

    init_timer(&priv->rateadapter_timer);
        priv->rateadapter_timer.data = (unsigned long)dev;
        priv->rateadapter_timer.function = timer_rate_adaptive;
		priv->RateAdaptivePeriod= RATE_ADAPTIVE_TIMER_PERIOD;
		priv->bEnhanceTxPwr=false;

	priv->ieee80211->softmac_hard_start_xmit = rtl8180_hard_start_xmit;
	priv->ieee80211->set_chan = rtl8180_set_chan;
	priv->ieee80211->link_change = rtl8180_link_change;
	priv->ieee80211->softmac_data_hard_start_xmit = rtl8180_hard_data_xmit;
	priv->ieee80211->data_hard_stop = rtl8180_data_hard_stop;
	priv->ieee80211->data_hard_resume = rtl8180_data_hard_resume;

        priv->ieee80211->init_wmmparam_flag = 0;

	priv->ieee80211->start_send_beacons = rtl8180_start_tx_beacon;
	priv->ieee80211->stop_send_beacons = rtl8180_beacon_tx_disable;
	priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD;

	priv->MWIEnable = 0;

	priv->ShortRetryLimit = 7;
	priv->LongRetryLimit = 7;
	priv->EarlyRxThreshold = 7;

	priv->CSMethod = (0x01 << 29);

	priv->TransmitConfig	=
									1<<TCR_DurProcMode_OFFSET |		//for RTL8185B, duration setting by HW
									(7<<TCR_MXDMA_OFFSET) |	// Max DMA Burst Size per Tx DMA Burst, 7: reservied.
									(priv->ShortRetryLimit<<TCR_SRL_OFFSET) |	// Short retry limit
									(priv->LongRetryLimit<<TCR_LRL_OFFSET) |	// Long retry limit
									(0 ? TCR_SAT : 0);	// FALSE: HW provies PLCP length and LENGEXT, TURE: SW proiveds them

	priv->ReceiveConfig	=
								RCR_AMF | RCR_ADF |				//accept management/data
								RCR_ACF |						//accept control frame for SW AP needs PS-poll, 2005.07.07, by rcnjko.
								RCR_AB | RCR_AM | RCR_APM |		//accept BC/MC/UC
								(7<<RCR_MXDMA_OFFSET) | // Max DMA Burst Size per Rx DMA Burst, 7: unlimited.
								(priv->EarlyRxThreshold<<RCR_FIFO_OFFSET) | // Rx FIFO Threshold, 7: No Rx threshold.
								(priv->EarlyRxThreshold == 7 ? RCR_ONLYERLPKT:0);

	priv->IntrMask		= IMR_TMGDOK | IMR_TBDER | IMR_THPDER |
								IMR_THPDER | IMR_THPDOK |
								IMR_TVODER | IMR_TVODOK |
								IMR_TVIDER | IMR_TVIDOK |
								IMR_TBEDER | IMR_TBEDOK |
								IMR_TBKDER | IMR_TBKDOK |
								IMR_RDU |						// To handle the defragmentation not enough Rx descriptors case. Annie, 2006-03-27.
								IMR_RER | IMR_ROK |
								IMR_RQoSOK; // <NOTE> ROK and RQoSOK are mutually exclusive, so, we must handle RQoSOK interrupt to receive QoS frames, 2005.12.09, by rcnjko.

	priv->InitialGain = 6;

	hw_version =( read_nic_dword(dev, TCR) & TCR_HWVERID_MASK)>>TCR_HWVERID_SHIFT;

	switch (hw_version){
		case HW_VERID_R8185B_B:
                        priv->card_8185 = VERSION_8187S_C;
		        DMESG("MAC controller is a RTL8187SE b/g");
			priv->phy_ver = 2;
			break;
		case HW_VERID_R8185_ABC:
			DMESG("MAC controller is a RTL8185 b/g");
			priv->card_8185 = 1;
			/* you should not find a card with 8225 PHY ver < C*/
			priv->phy_ver = 2;
			break;
		case HW_VERID_R8185_D:
			DMESG("MAC controller is a RTL8185 b/g (V. D)");
			priv->card_8185 = 2;
			/* you should not find a card with 8225 PHY ver < C*/
			priv->phy_ver = 2;
			break;
		case HW_VERID_R8180_ABCD:
			DMESG("MAC controller is a RTL8180");
			priv->card_8185 = 0;
			break;
		case HW_VERID_R8180_F:
			DMESG("MAC controller is a RTL8180 (v. F)");
			priv->card_8185 = 0;
			break;
		default:
			DMESGW("MAC chip not recognized: version %x. Assuming RTL8180",hw_version);
			priv->card_8185 = 0;
			break;
	}

	if(priv->card_8185){
		priv->ieee80211->modulation |= IEEE80211_OFDM_MODULATION;
		priv->ieee80211->short_slot = 1;
	}
	/* you should not found any 8185 Ver B Card */
	priv->card_8185_Bversion = 0;

	// just for sync 85
	priv->card_type = PCI;
        DMESG("This is a PCI NIC");
	priv->enable_gpio0 = 0;

	usValue = eprom_read(dev, EEPROM_SW_REVD_OFFSET);
	DMESG("usValue is 0x%x\n",usValue);
	//3Read AntennaDiversity

	// SW Antenna Diversity.
	if ((usValue & EEPROM_SW_AD_MASK) != EEPROM_SW_AD_ENABLE)
		priv->EEPROMSwAntennaDiversity = false;
	else
		priv->EEPROMSwAntennaDiversity = true;

	// Default Antenna to use.
	if ((usValue & EEPROM_DEF_ANT_MASK) != EEPROM_DEF_ANT_1)
		priv->EEPROMDefaultAntenna1 = false;
	else
		priv->EEPROMDefaultAntenna1 = true;

	if( priv->RegSwAntennaDiversityMechanism == 0 ) // Auto
		/* 0: default from EEPROM. */
		priv->bSwAntennaDiverity = priv->EEPROMSwAntennaDiversity;
	else
		/* 1:disable antenna diversity, 2: enable antenna diversity. */
		priv->bSwAntennaDiverity = ((priv->RegSwAntennaDiversityMechanism == 1)? false : true);

	if (priv->RegDefaultAntenna == 0)
		/* 0: default from EEPROM. */
		priv->bDefaultAntenna1 = priv->EEPROMDefaultAntenna1;
	else
		/* 1: main, 2: aux. */
		priv->bDefaultAntenna1 = ((priv->RegDefaultAntenna== 2) ? true : false);

	/* rtl8185 can calc plcp len in HW.*/
	priv->hw_plcp_len = 1;

	priv->plcp_preamble_mode = 2;
	/*the eeprom type is stored in RCR register bit #6 */
	if (RCR_9356SEL & read_nic_dword(dev, RCR))
		priv->epromtype=EPROM_93c56;
	else
		priv->epromtype=EPROM_93c46;

	dev->dev_addr[0]=eprom_read(dev,MAC_ADR) & 0xff;
	dev->dev_addr[1]=(eprom_read(dev,MAC_ADR) & 0xff00)>>8;
	dev->dev_addr[2]=eprom_read(dev,MAC_ADR+1) & 0xff;
	dev->dev_addr[3]=(eprom_read(dev,MAC_ADR+1) & 0xff00)>>8;
	dev->dev_addr[4]=eprom_read(dev,MAC_ADR+2) & 0xff;
	dev->dev_addr[5]=(eprom_read(dev,MAC_ADR+2) & 0xff00)>>8;

	for(i=1,j=0; i<14; i+=2,j++){
		word = eprom_read(dev,EPROM_TXPW_CH1_2 + j);
		priv->chtxpwr[i]=word & 0xff;
		priv->chtxpwr[i+1]=(word & 0xff00)>>8;
	}
	if(priv->card_8185){
		for(i=1,j=0; i<14; i+=2,j++){
			word = eprom_read(dev,EPROM_TXPW_OFDM_CH1_2 + j);
			priv->chtxpwr_ofdm[i]=word & 0xff;
			priv->chtxpwr_ofdm[i+1]=(word & 0xff00)>>8;
		}
	}

	//3Read crystal calibtration and thermal meter indication on 87SE.

	// By SD3 SY's request. Added by Roger, 2007.12.11.

	tmpu16 = eprom_read(dev, EEPROM_RSV>>1);

		// Crystal calibration for Xin and Xout resp.
		priv->XtalCal_Xout = tmpu16 & EEPROM_XTAL_CAL_XOUT_MASK; // 0~7.5pF
		priv->XtalCal_Xin = (tmpu16 & EEPROM_XTAL_CAL_XIN_MASK)>>4; // 0~7.5pF
		if((tmpu16 & EEPROM_XTAL_CAL_ENABLE)>>12)
			priv->bXtalCalibration = true;

		// Thermal meter reference indication.
		priv->ThermalMeter =  (u8)((tmpu16 & EEPROM_THERMAL_METER_MASK)>>8);
		if((tmpu16 & EEPROM_THERMAL_METER_ENABLE)>>13)
			priv->bTxPowerTrack = true;

	word = eprom_read(dev,EPROM_TXPW_BASE);
	priv->cck_txpwr_base = word & 0xf;
	priv->ofdm_txpwr_base = (word>>4) & 0xf;

	version = eprom_read(dev,EPROM_VERSION);
	DMESG("EEPROM version %x",version);
	if( (!priv->card_8185) && version < 0x0101){
		DMESG ("EEPROM version too old, assuming defaults");
		DMESG ("If you see this message *plase* send your \
DMESG output to andreamrl@tiscali.it THANKS");
		priv->digphy=1;
		priv->antb=0;
		priv->diversity=1;
		priv->cs_treshold=0xc;
		priv->rcr_csense=1;
		priv->rf_chip=RFCHIPID_PHILIPS;
	}else{
		if(!priv->card_8185){
			u8 rfparam = eprom_read(dev,RF_PARAM);
			DMESG("RfParam: %x",rfparam);

			priv->digphy = rfparam & (1<<RF_PARAM_DIGPHY_SHIFT) ? 0:1;
			priv->antb =  rfparam & (1<<RF_PARAM_ANTBDEFAULT_SHIFT) ? 1:0;

			priv->rcr_csense = (rfparam & RF_PARAM_CARRIERSENSE_MASK) >>
					RF_PARAM_CARRIERSENSE_SHIFT;

			priv->diversity =
				(read_nic_byte(dev,CONFIG2)&(1<<CONFIG2_ANTENNA_SHIFT)) ? 1:0;
		}else{
			priv->rcr_csense = 3;
		}

		priv->cs_treshold = (eprom_read(dev,ENERGY_TRESHOLD)&0xff00) >>8;

		priv->rf_chip = 0xff & eprom_read(dev,RFCHIPID);
	}

	priv->rf_chip = RF_ZEBRA4;
	priv->rf_sleep = rtl8225z4_rf_sleep;
	priv->rf_wakeup = rtl8225z4_rf_wakeup;
	DMESGW("**PLEASE** REPORT SUCCESSFUL/UNSUCCESSFUL TO Realtek!");

	priv->rf_close = rtl8225z2_rf_close;
	priv->rf_init = rtl8225z2_rf_init;
	priv->rf_set_chan = rtl8225z2_rf_set_chan;
	priv->rf_set_sens = NULL;

	if(!priv->card_8185){
		if(priv->antb)
			DMESG ("Antenna B is default antenna");
		else
			DMESG ("Antenna A is default antenna");

		if(priv->diversity)
			DMESG ("Antenna diversity is enabled");
		else
			DMESG("Antenna diversity is disabled");

		DMESG("Carrier sense %d",priv->rcr_csense);
	}

	if (0!=alloc_rx_desc_ring(dev, priv->rxbuffersize, priv->rxringcount))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				  TX_MANAGEPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				 TX_BKPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				 TX_BEPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				  TX_VIPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				  TX_VOPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txringcount,
				  TX_HIGHPRIORITY_RING_ADDR))
		return -ENOMEM;

	if (0!=alloc_tx_desc_ring(dev, priv->txbuffsize, priv->txbeaconcount,
				  TX_BEACON_RING_ADDR))
		return -ENOMEM;

	if(!priv->card_8185){
		if(read_nic_byte(dev, CONFIG0) & (1<<CONFIG0_WEP40_SHIFT))
			DMESG ("40-bit WEP is supported in hardware");
		else
			DMESG ("40-bit WEP is NOT supported in hardware");

		if(read_nic_byte(dev,CONFIG0) & (1<<CONFIG0_WEP104_SHIFT))
			DMESG ("104-bit WEP is supported in hardware");
		else
			DMESG ("104-bit WEP is NOT supported in hardware");
	}
#if !defined(SA_SHIRQ)
        if(request_irq(dev->irq, (void *)rtl8180_interrupt, IRQF_SHARED, dev->name, dev)){
#else
        if(request_irq(dev->irq, (void *)rtl8180_interrupt, SA_SHIRQ, dev->name, dev)){
#endif
                DMESGE("Error allocating IRQ %d",dev->irq);
                return -1;
	}else{
		priv->irq=dev->irq;
		DMESG("IRQ %d",dev->irq);
	}

	return 0;
}

void rtl8180_no_hw_wep(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	if (!priv->card_8185) {
		u8 security;

		security  = read_nic_byte(dev, SECURITY);
		security &=~(1<<SECURITY_WEP_TX_ENABLE_SHIFT);
		security &=~(1<<SECURITY_WEP_RX_ENABLE_SHIFT);

		write_nic_byte(dev, SECURITY, security);
	}
}

void rtl8180_set_hw_wep(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	u8 pgreg;
	u8 security;
	u32 key0_word4;

	pgreg=read_nic_byte(dev, PGSELECT);
	write_nic_byte(dev, PGSELECT, pgreg &~ (1<<PGSELECT_PG_SHIFT));

	key0_word4 = read_nic_dword(dev, KEY0+4+4+4);
	key0_word4 &= ~ 0xff;
	key0_word4 |= priv->key0[3]& 0xff;
	write_nic_dword(dev,KEY0,(priv->key0[0]));
	write_nic_dword(dev,KEY0+4,(priv->key0[1]));
	write_nic_dword(dev,KEY0+4+4,(priv->key0[2]));
	write_nic_dword(dev,KEY0+4+4+4,(key0_word4));

	security  = read_nic_byte(dev,SECURITY);
	security |= (1<<SECURITY_WEP_TX_ENABLE_SHIFT);
	security |= (1<<SECURITY_WEP_RX_ENABLE_SHIFT);
	security &= ~ SECURITY_ENCRYP_MASK;
	security |= (SECURITY_ENCRYP_104<<SECURITY_ENCRYP_SHIFT);

	write_nic_byte(dev, SECURITY, security);

	DMESG("key %x %x %x %x",read_nic_dword(dev,KEY0+4+4+4),
	      read_nic_dword(dev,KEY0+4+4),read_nic_dword(dev,KEY0+4),
	      read_nic_dword(dev,KEY0));
}


void rtl8185_rf_pins_enable(struct net_device *dev)
{
//	u16 tmp;
//	tmp = read_nic_word(dev, RFPinsEnable);
	write_nic_word(dev, RFPinsEnable, 0x1fff);// | tmp);
}

void rtl8185_set_anaparam2(struct net_device *dev, u32 a)
{
	u8 conf3;

	rtl8180_set_mode(dev, EPROM_CMD_CONFIG);

	conf3 = read_nic_byte(dev, CONFIG3);
	write_nic_byte(dev, CONFIG3, conf3 | (1<<CONFIG3_ANAPARAM_W_SHIFT));
	write_nic_dword(dev, ANAPARAM2, a);

	conf3 = read_nic_byte(dev, CONFIG3);
	write_nic_byte(dev, CONFIG3, conf3 &~(1<<CONFIG3_ANAPARAM_W_SHIFT));
	rtl8180_set_mode(dev, EPROM_CMD_NORMAL);
}

void rtl8180_set_anaparam(struct net_device *dev, u32 a)
{
	u8 conf3;

	rtl8180_set_mode(dev, EPROM_CMD_CONFIG);

	conf3 = read_nic_byte(dev, CONFIG3);
	write_nic_byte(dev, CONFIG3, conf3 | (1<<CONFIG3_ANAPARAM_W_SHIFT));
	write_nic_dword(dev, ANAPARAM, a);

	conf3 = read_nic_byte(dev, CONFIG3);
	write_nic_byte(dev, CONFIG3, conf3 &~(1<<CONFIG3_ANAPARAM_W_SHIFT));
	rtl8180_set_mode(dev, EPROM_CMD_NORMAL);
}

void rtl8185_tx_antenna(struct net_device *dev, u8 ant)
{
	write_nic_byte(dev, TX_ANTENNA, ant);
	force_pci_posting(dev);
	mdelay(1);
}

void rtl8185_write_phy(struct net_device *dev, u8 adr, u32 data)
{
	u32 phyw;

	adr |= 0x80;

	phyw= ((data<<8) | adr);

	// Note that, we must write 0xff7c after 0x7d-0x7f to write BB register.
	write_nic_byte(dev, 0x7f, ((phyw & 0xff000000) >> 24));
	write_nic_byte(dev, 0x7e, ((phyw & 0x00ff0000) >> 16));
	write_nic_byte(dev, 0x7d, ((phyw & 0x0000ff00) >> 8));
	write_nic_byte(dev, 0x7c, ((phyw & 0x000000ff) ));

	/* this is ok to fail when we write AGC table. check for AGC table might be
	 * done by masking with 0x7f instead of 0xff
	 */
	//if(phyr != (data&0xff)) DMESGW("Phy write timeout %x %x %x", phyr, data,adr);
}

inline void write_phy_ofdm (struct net_device *dev, u8 adr, u32 data)
{
	data = data & 0xff;
	rtl8185_write_phy(dev, adr, data);
}

void write_phy_cck (struct net_device *dev, u8 adr, u32 data)
{
	data = data & 0xff;
	rtl8185_write_phy(dev, adr, data | 0x10000);
}

/* 70*3 = 210 ms
 * I hope this is enougth
 */
#define MAX_PHY 70
void write_phy(struct net_device *dev, u8 adr, u8 data)
{
	u32 phy;
	int i;

	phy = 0xff0000;
	phy |= adr;
	phy |= 0x80; /* this should enable writing */
	phy |= (data<<8);

	//PHY_ADR, PHY_R and PHY_W  are contig and treated as one dword
	write_nic_dword(dev,PHY_ADR, phy);

	phy= 0xffff00;
	phy |= adr;

	write_nic_dword(dev,PHY_ADR, phy);
	for(i=0;i<MAX_PHY;i++){
		phy=read_nic_dword(dev,PHY_ADR);
		phy= phy & 0xff0000;
		phy= phy >> 16;
		if(phy == data){ //SUCCESS!
			force_pci_posting(dev);
			mdelay(3); //random value
			return;
		}else{
			force_pci_posting(dev);
			mdelay(3); //random value
		}
	}
	DMESGW ("Phy writing %x %x failed!", adr,data);
}

void rtl8185_set_rate(struct net_device *dev)
{
	int i;
	u16 word;
	int basic_rate,min_rr_rate,max_rr_rate;

	basic_rate = ieeerate2rtlrate(240);
	min_rr_rate = ieeerate2rtlrate(60);
	max_rr_rate = ieeerate2rtlrate(240);

	write_nic_byte(dev, RESP_RATE,
			max_rr_rate<<MAX_RESP_RATE_SHIFT| min_rr_rate<<MIN_RESP_RATE_SHIFT);

	word  = read_nic_word(dev, BRSR);
	word &= ~BRSR_MBR_8185;

	for(i=0;i<=basic_rate;i++)
		word |= (1<<i);

	write_nic_word(dev, BRSR, word);
}

void rtl8180_adapter_start(struct net_device *dev)
{
        struct r8180_priv *priv = ieee80211_priv(dev);
	u32 anaparam;
	u16 word;
	u8 config3;

	rtl8180_rtx_disable(dev);
	rtl8180_reset(dev);

	/* enable beacon timeout, beacon TX ok and err
	 * LP tx ok and err, HP TX ok and err, NP TX ok and err,
	 * RX ok and ERR, and GP timer */
	priv->irq_mask = 0x6fcf;

	priv->dma_poll_mask = 0;

	rtl8180_beacon_tx_disable(dev);

	if(priv->card_type == CARDBUS ){
		config3=read_nic_byte(dev, CONFIG3);
		write_nic_byte(dev,CONFIG3,config3 | CONFIG3_FuncRegEn);
		write_nic_word(dev,FEMR, FEMR_INTR | FEMR_WKUP | FEMR_GWAKE |
			read_nic_word(dev, FEMR));
	}
	rtl8180_set_mode(dev, EPROM_CMD_CONFIG);
	write_nic_dword(dev, MAC0, ((u32*)dev->dev_addr)[0]);
	write_nic_word(dev, MAC4, ((u32*)dev->dev_addr)[1] & 0xffff );
	rtl8180_set_mode(dev, EPROM_CMD_NORMAL);

	rtl8180_update_msr(dev);

	if(!priv->card_8185){
		anaparam  = eprom_read(dev,EPROM_ANAPARAM_ADDRLWORD);
		anaparam |= eprom_read(dev,EPROM_ANAPARAM_ADDRHWORD)<<16;

		rtl8180_set_anaparam(dev,anaparam);
	}
	/* These might be unnecessary since we do in rx_enable / tx_enable */
	fix_rx_fifo(dev);
	fix_tx_fifo(dev);

	rtl8180_set_mode(dev, EPROM_CMD_CONFIG);

	/*
	   The following is very strange. seems to be that 1 means test mode,
	   but we need to acknolwledges the nic when a packet is ready
	   altought we set it to 0
	*/

	write_nic_byte(dev,
		       CONFIG2, read_nic_byte(dev,CONFIG2) &~\
		       (1<<CONFIG2_DMA_POLLING_MODE_SHIFT));
	//^the nic isn't in test mode
	if(priv->card_8185)
			write_nic_byte(dev,
		       CONFIG2, read_nic_byte(dev,CONFIG2)|(1<<4));

	rtl8180_set_mode(dev,EPROM_CMD_NORMAL);

	write_nic_dword(dev,INT_TIMEOUT,0);

	if(!priv->card_8185)
	{
		/*
		experimental - this might be needed to calibrate AGC,
		anyway it shouldn't hurt
		*/
		write_nic_byte(dev, CONFIG5,
			read_nic_byte(dev, CONFIG5) | (1<<AGCRESET_SHIFT));
		read_nic_byte(dev, CONFIG5);
		udelay(15);
		write_nic_byte(dev, CONFIG5,
			read_nic_byte(dev, CONFIG5) &~ (1<<AGCRESET_SHIFT));
	}else{
		write_nic_byte(dev, WPA_CONFIG, 0);
		//write_nic_byte(dev, TESTR, 0xd);
	}

	rtl8180_no_hw_wep(dev);

	if(priv->card_8185){
		rtl8185_set_rate(dev);
		write_nic_byte(dev, RATE_FALLBACK, 0x81);
	}else{
		word  = read_nic_word(dev, BRSR);
		word &= ~BRSR_MBR;
		word &= ~BRSR_BPLCP;
		word |= ieeerate2rtlrate(priv->ieee80211->basic_rate);
		word |= 0x0f;
		write_nic_word(dev, BRSR, word);
	}

	if(priv->card_8185){
		write_nic_byte(dev, GP_ENABLE,read_nic_byte(dev, GP_ENABLE) & ~(1<<6));

		//FIXME cfg 3 ClkRun enable - isn't it ReadOnly ?
		rtl8180_set_mode(dev, EPROM_CMD_CONFIG);
		write_nic_byte(dev,CONFIG3, read_nic_byte(dev, CONFIG3)
			       | (1 << CONFIG3_CLKRUN_SHIFT));
		rtl8180_set_mode(dev, EPROM_CMD_NORMAL);
	}

	priv->rf_init(dev);

	if(priv->rf_set_sens != NULL)
		priv->rf_set_sens(dev,priv->sens);
	rtl8180_irq_enable(dev);

	netif_start_queue(dev);
}

/* this configures registers for beacon tx and enables it via
 * rtl8180_beacon_tx_enable(). rtl8180_beacon_tx_disable() might
 * be used to stop beacon transmission
 */
void rtl8180_start_tx_beacon(struct net_device *dev)
{
	u16 word;

	DMESG("Enabling beacon TX");
	rtl8180_prepare_beacon(dev);
	rtl8180_irq_disable(dev);
	rtl8180_beacon_tx_enable(dev);

	word = read_nic_word(dev, AtimWnd) &~ AtimWnd_AtimWnd;
	write_nic_word(dev, AtimWnd,word);// word |=

	word  = read_nic_word(dev, BintrItv);
	word &= ~BintrItv_BintrItv;
	word |= 1000;/*priv->ieee80211->current_network.beacon_interval *
		((priv->txbeaconcount > 1)?(priv->txbeaconcount-1):1);
	// FIXME: check if correct ^^ worked with 0x3e8;
	*/
	write_nic_word(dev, BintrItv, word);

	rtl8180_set_mode(dev, EPROM_CMD_NORMAL);

        rtl8185b_irq_enable(dev);
}

static struct net_device_stats *rtl8180_stats(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	return &priv->ieee80211->stats;
}
//
// Change current and default preamble mode.
// 2005.01.06, by rcnjko.
//
bool
MgntActSet_802_11_PowerSaveMode(
	struct r8180_priv *priv,
	RT_PS_MODE		rtPsMode
)
{
	// Currently, we do not change power save mode on IBSS mode.
	if(priv->ieee80211->iw_mode == IW_MODE_ADHOC)
		return false;

	priv->ieee80211->ps = rtPsMode;

	return true;
}

void LeisurePSEnter(struct r8180_priv *priv)
{
	if (priv->bLeisurePs) {
		if (priv->ieee80211->ps == IEEE80211_PS_DISABLED)
			MgntActSet_802_11_PowerSaveMode(priv, IEEE80211_PS_MBCAST|IEEE80211_PS_UNICAST);//IEEE80211_PS_ENABLE
	}
}

void LeisurePSLeave(struct r8180_priv *priv)
{
	if (priv->bLeisurePs) {
		if (priv->ieee80211->ps != IEEE80211_PS_DISABLED)
			MgntActSet_802_11_PowerSaveMode(priv, IEEE80211_PS_DISABLED);
	}
}

void rtl8180_hw_wakeup_wq (struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,hw_wakeup_wq);
	struct net_device *dev = ieee->dev;

	rtl8180_hw_wakeup(dev);
}

void rtl8180_hw_sleep_wq (struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
        struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,hw_sleep_wq);
        struct net_device *dev = ieee->dev;

        rtl8180_hw_sleep_down(dev);
}

static void MgntLinkKeepAlive(struct r8180_priv *priv )
{
	if (priv->keepAliveLevel == 0)
		return;

	if(priv->ieee80211->state == IEEE80211_LINKED)
	{
		//
		// Keep-Alive.
		//

		if ( (priv->keepAliveLevel== 2) ||
			(priv->link_detect.LastNumTxUnicast == priv->NumTxUnicast &&
			priv->link_detect.LastNumRxUnicast == priv->ieee80211->NumRxUnicast )
			)
		{
			priv->link_detect.IdleCount++;

			//
			// Send a Keep-Alive packet packet to AP if we had been idle for a while.
			//
			if(priv->link_detect.IdleCount >= ((KEEP_ALIVE_INTERVAL / CHECK_FOR_HANG_PERIOD)-1) )
			{
				priv->link_detect.IdleCount = 0;
				ieee80211_sta_ps_send_null_frame(priv->ieee80211, false);
			}
		}
		else
		{
			priv->link_detect.IdleCount = 0;
		}
		priv->link_detect.LastNumTxUnicast = priv->NumTxUnicast;
		priv->link_detect.LastNumRxUnicast = priv->ieee80211->NumRxUnicast;
	}
}

static u8 read_acadapter_file(char *filename);

void rtl8180_watch_dog(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	bool bEnterPS = false;
	bool bBusyTraffic = false;
	u32 TotalRxNum = 0;
	u16 SlotIndex = 0;
	u16 i = 0;
	if(priv->ieee80211->actscanning == false){
		if((priv->ieee80211->iw_mode != IW_MODE_ADHOC) && (priv->ieee80211->state == IEEE80211_NOLINK) && (priv->ieee80211->beinretry == false) && (priv->eRFPowerState == eRfOn)){
			IPSEnter(dev);
		}
	}
	//YJ,add,080828,for link state check
	if((priv->ieee80211->state == IEEE80211_LINKED) && (priv->ieee80211->iw_mode == IW_MODE_INFRA)){
		SlotIndex = (priv->link_detect.SlotIndex++) % priv->link_detect.SlotNum;
		priv->link_detect.RxFrameNum[SlotIndex] = priv->ieee80211->NumRxDataInPeriod + priv->ieee80211->NumRxBcnInPeriod;
		for( i=0; i<priv->link_detect.SlotNum; i++ )
			TotalRxNum+= priv->link_detect.RxFrameNum[i];

		if(TotalRxNum == 0){
			priv->ieee80211->state = IEEE80211_ASSOCIATING;
			queue_work(priv->ieee80211->wq, &priv->ieee80211->associate_procedure_wq);
		}
	}

	//YJ,add,080828,for KeepAlive
	MgntLinkKeepAlive(priv);

	//YJ,add,080828,for LPS
#ifdef ENABLE_LPS
	if (priv->PowerProfile == POWER_PROFILE_BATTERY)
		priv->bLeisurePs = true;
	else if (priv->PowerProfile == POWER_PROFILE_AC) {
		LeisurePSLeave(priv);
		priv->bLeisurePs= false;
	}

	if(priv->ieee80211->state == IEEE80211_LINKED){
		priv->link_detect.NumRxOkInPeriod = priv->ieee80211->NumRxDataInPeriod;
		//printk("TxOk=%d RxOk=%d\n", priv->link_detect.NumTxOkInPeriod, priv->link_detect.NumRxOkInPeriod);
		if(	priv->link_detect.NumRxOkInPeriod> 666 ||
			priv->link_detect.NumTxOkInPeriod> 666 ) {
			bBusyTraffic = true;
		}
		if(((priv->link_detect.NumRxOkInPeriod + priv->link_detect.NumTxOkInPeriod) > 8)
			|| (priv->link_detect.NumRxOkInPeriod > 2)) {
			bEnterPS= false;
		} else
			bEnterPS= true;

		if (bEnterPS)
			LeisurePSEnter(priv);
		else
			LeisurePSLeave(priv);
	} else
		LeisurePSLeave(priv);
#endif
	priv->link_detect.bBusyTraffic = bBusyTraffic;
	priv->link_detect.NumRxOkInPeriod = 0;
	priv->link_detect.NumTxOkInPeriod = 0;
	priv->ieee80211->NumRxDataInPeriod = 0;
	priv->ieee80211->NumRxBcnInPeriod = 0;
}

int _rtl8180_up(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	priv->up=1;

	DMESG("Bringing up iface");
	rtl8185b_adapter_start(dev);
	rtl8185b_rx_enable(dev);
	rtl8185b_tx_enable(dev);
	if(priv->bInactivePs){
		if(priv->ieee80211->iw_mode == IW_MODE_ADHOC)
			IPSLeave(dev);
	}
#ifdef RATE_ADAPT
       timer_rate_adaptive((unsigned long)dev);
#endif
	watch_dog_adaptive((unsigned long)dev);
#ifdef SW_ANTE
        if(priv->bSwAntennaDiverity)
			SwAntennaDiversityTimerCallback(dev);
#endif
	ieee80211_softmac_start_protocol(priv->ieee80211);

	return 0;
}

int rtl8180_open(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	int ret;

	down(&priv->wx_sem);
	ret = rtl8180_up(dev);
	up(&priv->wx_sem);
	return ret;
}

int rtl8180_up(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	if (priv->up == 1) return -1;

	return _rtl8180_up(dev);
}

int rtl8180_close(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	int ret;

	down(&priv->wx_sem);
	ret = rtl8180_down(dev);
	up(&priv->wx_sem);

	return ret;
}

int rtl8180_down(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	if (priv->up == 0)
		return -1;

	priv->up=0;

	ieee80211_softmac_stop_protocol(priv->ieee80211);
	/* FIXME */
	if (!netif_queue_stopped(dev))
		netif_stop_queue(dev);
	rtl8180_rtx_disable(dev);
	rtl8180_irq_disable(dev);
	del_timer_sync(&priv->watch_dog_timer);
	del_timer_sync(&priv->rateadapter_timer);
	cancel_delayed_work(&priv->ieee80211->rate_adapter_wq);
	cancel_delayed_work(&priv->ieee80211->hw_wakeup_wq);
	cancel_delayed_work(&priv->ieee80211->hw_sleep_wq);
	cancel_delayed_work(&priv->ieee80211->hw_dig_wq);
	cancel_delayed_work(&priv->ieee80211->tx_pw_wq);
	del_timer_sync(&priv->SwAntennaDiversityTimer);
	SetZebraRFPowerState8185(dev,eRfOff);
	memset(&(priv->ieee80211->current_network),0,sizeof(struct ieee80211_network));
	priv->ieee80211->state = IEEE80211_NOLINK;
	return 0;
}

void rtl8180_restart_wq(struct work_struct *work)
{
	struct r8180_priv *priv = container_of(work, struct r8180_priv, reset_wq);
	struct net_device *dev = priv->dev;

	down(&priv->wx_sem);

	rtl8180_commit(dev);

	up(&priv->wx_sem);
}

void rtl8180_restart(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	schedule_work(&priv->reset_wq);
}

void rtl8180_commit(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);

	if (priv->up == 0)
		return ;

	del_timer_sync(&priv->watch_dog_timer);
	del_timer_sync(&priv->rateadapter_timer);
	cancel_delayed_work(&priv->ieee80211->rate_adapter_wq);
	cancel_delayed_work(&priv->ieee80211->hw_wakeup_wq);
	cancel_delayed_work(&priv->ieee80211->hw_sleep_wq);
	cancel_delayed_work(&priv->ieee80211->hw_dig_wq);
	cancel_delayed_work(&priv->ieee80211->tx_pw_wq);
	del_timer_sync(&priv->SwAntennaDiversityTimer);
	ieee80211_softmac_stop_protocol(priv->ieee80211);
	rtl8180_irq_disable(dev);
	rtl8180_rtx_disable(dev);
	_rtl8180_up(dev);
}

static void r8180_set_multicast(struct net_device *dev)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	short promisc;

	promisc = (dev->flags & IFF_PROMISC) ? 1:0;

	if (promisc != priv->promisc)
		rtl8180_restart(dev);

	priv->promisc = promisc;
}

int r8180_set_mac_adr(struct net_device *dev, void *mac)
{
	struct r8180_priv *priv = ieee80211_priv(dev);
	struct sockaddr *addr = mac;

	down(&priv->wx_sem);

	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);

	if(priv->ieee80211->iw_mode == IW_MODE_MASTER)
		memcpy(priv->ieee80211->current_network.bssid, dev->dev_addr, ETH_ALEN);

	if (priv->up) {
		rtl8180_down(dev);
		rtl8180_up(dev);
	}

	up(&priv->wx_sem);

	return 0;
}

/* based on ipw2200 driver */
int rtl8180_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	struct iwreq *wrq = (struct iwreq *) rq;
	int ret=-1;

	switch (cmd) {
	case RTL_IOCTL_WPA_SUPPLICANT:
		ret = ieee80211_wpa_supplicant_ioctl(priv->ieee80211, &wrq->u.data);
		return ret;
	default:
		return -EOPNOTSUPP;
	}

	return -EOPNOTSUPP;
}

static const struct net_device_ops rtl8180_netdev_ops = {
	.ndo_open		= rtl8180_open,
	.ndo_stop		= rtl8180_close,
	.ndo_get_stats		= rtl8180_stats,
	.ndo_tx_timeout		= rtl8180_restart,
	.ndo_do_ioctl		= rtl8180_ioctl,
	.ndo_set_multicast_list	= r8180_set_multicast,
	.ndo_set_mac_address	= r8180_set_mac_adr,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_start_xmit		= ieee80211_xmit,
};

static int __devinit rtl8180_pci_probe(struct pci_dev *pdev,
				       const struct pci_device_id *id)
{
	unsigned long ioaddr = 0;
	struct net_device *dev = NULL;
	struct r8180_priv *priv= NULL;
	u8 unit = 0;

	unsigned long pmem_start, pmem_len, pmem_flags;

	DMESG("Configuring chip resources");

	if( pci_enable_device (pdev) ){
		DMESG("Failed to enable PCI device");
		return -EIO;
	}

	pci_set_master(pdev);
	pci_set_dma_mask(pdev, 0xffffff00ULL);
	pci_set_consistent_dma_mask(pdev,0xffffff00ULL);
	dev = alloc_ieee80211(sizeof(struct r8180_priv));
	if (!dev)
		return -ENOMEM;
	priv = ieee80211_priv(dev);
	priv->ieee80211 = netdev_priv(dev);

	pci_set_drvdata(pdev, dev);
	SET_NETDEV_DEV(dev, &pdev->dev);

	priv = ieee80211_priv(dev);
	priv->pdev = pdev;

	pmem_start = pci_resource_start(pdev, 1);
	pmem_len = pci_resource_len(pdev, 1);
	pmem_flags = pci_resource_flags (pdev, 1);

	if (!(pmem_flags & IORESOURCE_MEM)) {
		DMESG("region #1 not a MMIO resource, aborting");
		goto fail;
	}

	if( ! request_mem_region(pmem_start, pmem_len, RTL8180_MODULE_NAME)) {
		DMESG("request_mem_region failed!");
		goto fail;
	}

	ioaddr = (unsigned long)ioremap_nocache( pmem_start, pmem_len);
	if( ioaddr == (unsigned long)NULL ){
		DMESG("ioremap failed!");
		goto fail1;
	}

	dev->mem_start = ioaddr; // shared mem start
	dev->mem_end = ioaddr + pci_resource_len(pdev, 0); // shared mem end

	pci_read_config_byte(pdev, 0x05, &unit);
	pci_write_config_byte(pdev, 0x05, unit & (~0x04));

	dev->irq = pdev->irq;
	priv->irq = 0;

	dev->netdev_ops = &rtl8180_netdev_ops;
	dev->wireless_handlers = &r8180_wx_handlers_def;

	dev->type=ARPHRD_ETHER;
	dev->watchdog_timeo = HZ*3; //added by david woo, 2007.12.13

	if (dev_alloc_name(dev, ifname) < 0){
                DMESG("Oops: devname already taken! Trying wlan%%d...\n");
		ifname = "wlan%d";
		dev_alloc_name(dev, ifname);
        }

	if(rtl8180_init(dev)!=0){
		DMESG("Initialization failed");
		goto fail1;
	}

	netif_carrier_off(dev);

	register_netdev(dev);

	rtl8180_proc_init_one(dev);

	DMESG("Driver probe completed\n");
	return 0;
fail1:
	if( dev->mem_start != (unsigned long)NULL ){
		iounmap( (void *)dev->mem_start );
		release_mem_region( pci_resource_start(pdev, 1),
				    pci_resource_len(pdev, 1) );
	}
fail:
	if(dev){
		if (priv->irq) {
			free_irq(dev->irq, dev);
			dev->irq=0;
		}
		free_ieee80211(dev);
	}

	pci_disable_device(pdev);

	DMESG("wlan driver load failed\n");
	pci_set_drvdata(pdev, NULL);
	return -ENODEV;
}

static void __devexit rtl8180_pci_remove(struct pci_dev *pdev)
{
	struct r8180_priv *priv;
	struct net_device *dev = pci_get_drvdata(pdev);

	if (dev) {
		unregister_netdev(dev);

		priv = ieee80211_priv(dev);

		rtl8180_proc_remove_one(dev);
		rtl8180_down(dev);
		priv->rf_close(dev);
		rtl8180_reset(dev);
		mdelay(10);

		if(priv->irq){
			DMESG("Freeing irq %d",dev->irq);
			free_irq(dev->irq, dev);
			priv->irq=0;
		}

		free_rx_desc_ring(dev);
		free_tx_desc_rings(dev);

		if( dev->mem_start != (unsigned long)NULL ){
			iounmap( (void *)dev->mem_start );
			release_mem_region( pci_resource_start(pdev, 1),
					    pci_resource_len(pdev, 1) );
		}

		free_ieee80211(dev);
	}
	pci_disable_device(pdev);

	DMESG("wlan driver removed\n");
}

/* fun with the built-in ieee80211 stack... */
extern int ieee80211_crypto_init(void);
extern void ieee80211_crypto_deinit(void);
extern int ieee80211_crypto_tkip_init(void);
extern void ieee80211_crypto_tkip_exit(void);
extern int ieee80211_crypto_ccmp_init(void);
extern void ieee80211_crypto_ccmp_exit(void);
extern int ieee80211_crypto_wep_init(void);
extern void ieee80211_crypto_wep_exit(void);

static int __init rtl8180_pci_module_init(void)
{
	int ret;

	ret = ieee80211_crypto_init();
	if (ret) {
		printk(KERN_ERR "ieee80211_crypto_init() failed %d\n", ret);
		return ret;
	}
	ret = ieee80211_crypto_tkip_init();
	if (ret) {
		printk(KERN_ERR "ieee80211_crypto_tkip_init() failed %d\n", ret);
		return ret;
	}
	ret = ieee80211_crypto_ccmp_init();
	if (ret) {
		printk(KERN_ERR "ieee80211_crypto_ccmp_init() failed %d\n", ret);
		return ret;
	}
	ret = ieee80211_crypto_wep_init();
	if (ret) {
		printk(KERN_ERR "ieee80211_crypto_wep_init() failed %d\n", ret);
		return ret;
	}

	printk(KERN_INFO "\nLinux kernel driver for RTL8180 \
/ RTL8185 based WLAN cards\n");
	printk(KERN_INFO "Copyright (c) 2004-2005, Andrea Merello\n");
	DMESG("Initializing module");
	DMESG("Wireless extensions version %d", WIRELESS_EXT);
	rtl8180_proc_module_init();

      if (pci_register_driver(&rtl8180_pci_driver)) {
		DMESG("No device found");
		return -ENODEV;
	}
	return 0;
}

static void __exit rtl8180_pci_module_exit(void)
{
	pci_unregister_driver (&rtl8180_pci_driver);
	rtl8180_proc_module_remove();
	ieee80211_crypto_tkip_exit();
	ieee80211_crypto_ccmp_exit();
	ieee80211_crypto_wep_exit();
	ieee80211_crypto_deinit();
	DMESG("Exiting");
}

void rtl8180_try_wake_queue(struct net_device *dev, int pri)
{
	unsigned long flags;
	short enough_desc;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);

	spin_lock_irqsave(&priv->tx_lock,flags);
	enough_desc = check_nic_enought_desc(dev,pri);
	spin_unlock_irqrestore(&priv->tx_lock,flags);

	if(enough_desc)
		ieee80211_wake_queue(priv->ieee80211);
}

void rtl8180_tx_isr(struct net_device *dev, int pri,short error)
{
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	u32 *tail; //tail virtual addr
	u32 *head; //head virtual addr
	u32 *begin;//start of ring virtual addr
	u32 *nicv; //nic pointer virtual addr
	u32 nic; //nic pointer physical addr
	u32 nicbegin;// start of ring physical addr
	unsigned long flag;
	/* physical addr are ok on 32 bits since we set DMA mask*/
	int offs;
	int j,i;
	int hd;
	if (error) priv->stats.txretry++; //tony 20060601
	spin_lock_irqsave(&priv->tx_lock,flag);
	switch(pri) {
	case MANAGE_PRIORITY:
		tail = priv->txmapringtail;
		begin = priv->txmapring;
		head = priv->txmapringhead;
		nic = read_nic_dword(dev,TX_MANAGEPRIORITY_RING_ADDR);
		nicbegin = priv->txmapringdma;
		break;
	case BK_PRIORITY:
		tail = priv->txbkpringtail;
		begin = priv->txbkpring;
		head = priv->txbkpringhead;
		nic = read_nic_dword(dev,TX_BKPRIORITY_RING_ADDR);
		nicbegin = priv->txbkpringdma;
		break;
	case BE_PRIORITY:
		tail = priv->txbepringtail;
		begin = priv->txbepring;
		head = priv->txbepringhead;
		nic = read_nic_dword(dev,TX_BEPRIORITY_RING_ADDR);
		nicbegin = priv->txbepringdma;
		break;
	case VI_PRIORITY:
		tail = priv->txvipringtail;
		begin = priv->txvipring;
		head = priv->txvipringhead;
		nic = read_nic_dword(dev,TX_VIPRIORITY_RING_ADDR);
		nicbegin = priv->txvipringdma;
		break;
	case VO_PRIORITY:
		tail = priv->txvopringtail;
		begin = priv->txvopring;
		head = priv->txvopringhead;
		nic = read_nic_dword(dev,TX_VOPRIORITY_RING_ADDR);
		nicbegin = priv->txvopringdma;
		break;
	case HI_PRIORITY:
		tail = priv->txhpringtail;
		begin = priv->txhpring;
		head = priv->txhpringhead;
		nic = read_nic_dword(dev,TX_HIGHPRIORITY_RING_ADDR);
		nicbegin = priv->txhpringdma;
		break;

	default:
		spin_unlock_irqrestore(&priv->tx_lock,flag);
		return ;
	}

	nicv = (u32*) ((nic - nicbegin) + (u8*)begin);
	if((head <= tail && (nicv > tail || nicv < head)) ||
		(head > tail && (nicv > tail && nicv < head))){
			DMESGW("nic has lost pointer");
			spin_unlock_irqrestore(&priv->tx_lock,flag);
			rtl8180_restart(dev);
			return;
		}

	/* we check all the descriptors between the head and the nic,
	 * but not the currenly pointed by the nic (the next to be txed)
	 * and the previous of the pointed (might be in process ??)
	*/
	offs = (nic - nicbegin);
	offs = offs / 8 /4;
	hd = (head - begin) /8;

	if(offs >= hd)
		j = offs - hd;
	else
		j = offs + (priv->txringcount -1 -hd);

	j-=2;
	if(j<0) j=0;

	for(i=0;i<j;i++)
	{
		if((*head) & (1<<31))
			break;
		if(((*head)&(0x10000000)) != 0){
			priv->CurrRetryCnt += (u16)((*head) & (0x000000ff));
			if (!error)
				priv->NumTxOkTotal++;
		}

		if (!error)
			priv->NumTxOkBytesTotal += (*(head+3)) & (0x00000fff);

		*head = *head &~ (1<<31);

		if((head - begin)/8 == priv->txringcount-1)
			head=begin;
		else
			head+=8;
	}

	/* the head has been moved to the last certainly TXed
	 * (or at least processed by the nic) packet.
	 * The driver take forcefully owning of all these packets
	 * If the packet previous of the nic pointer has been
	 * processed this doesn't matter: it will be checked
	 * here at the next round. Anyway if no more packet are
	 * TXed no memory leak occour at all.
	 */

	switch(pri) {
	case MANAGE_PRIORITY:
		priv->txmapringhead = head;

		if(priv->ack_tx_to_ieee){
			if(rtl8180_is_tx_queue_empty(dev)){
				priv->ack_tx_to_ieee = 0;
				ieee80211_ps_tx_ack(priv->ieee80211,!error);
			}
		}
		break;
	case BK_PRIORITY:
		priv->txbkpringhead = head;
		break;
	case BE_PRIORITY:
		priv->txbepringhead = head;
		break;
	case VI_PRIORITY:
		priv->txvipringhead = head;
		break;
	case VO_PRIORITY:
		priv->txvopringhead = head;
		break;
	case HI_PRIORITY:
		priv->txhpringhead = head;
		break;
	}

	spin_unlock_irqrestore(&priv->tx_lock,flag);
}

void rtl8180_tx_irq_wq(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct ieee80211_device * ieee = (struct ieee80211_device*)
	                                       container_of(dwork, struct ieee80211_device, watch_dog_wq);
	struct net_device *dev = ieee->dev;

	rtl8180_tx_isr(dev,MANAGE_PRIORITY,0);
}
irqreturn_t rtl8180_interrupt(int irq, void *netdev, struct pt_regs *regs)
{
	struct net_device *dev = (struct net_device *) netdev;
	struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
	unsigned long flags;
	u32 inta;

	/* We should return IRQ_NONE, but for now let me keep this */
	if(priv->irq_enabled == 0) return IRQ_HANDLED;

	spin_lock_irqsave(&priv->irq_th_lock,flags);

	//ISR: 4bytes
	inta = read_nic_dword(dev, ISR);// & priv->IntrMask;
	write_nic_dword(dev,ISR,inta); // reset int situation

	priv->stats.shints++;

	if(!inta){
		spin_unlock_irqrestore(&priv->irq_th_lock,flags);
		return IRQ_HANDLED;
	/*
	   most probably we can safely return IRQ_NONE,
	   but for now is better to avoid problems
	*/
	}

	if (inta == 0xffff) {
		/* HW disappared */
		spin_unlock_irqrestore(&priv->irq_th_lock, flags);
		return IRQ_HANDLED;
	}

	priv->stats.ints++;

	if(!netif_running(dev)) {
		spin_unlock_irqrestore(&priv->irq_th_lock,flags);
		return IRQ_HANDLED;
	}

	if (inta & ISR_TimeOut)
		write_nic_dword(dev, TimerInt, 0);

	if (inta & ISR_TBDOK)
		priv->stats.txbeacon++;

	if (inta & ISR_TBDER)
		priv->stats.txbeaconerr++;

	if (inta & IMR_TMGDOK)
		rtl8180_tx_isr(dev,MANAGE_PRIORITY,0);

	if(inta & ISR_THPDER){
		priv->stats.txhperr++;
		rtl8180_tx_isr(dev,HI_PRIORITY,1);
		priv->ieee80211->stats.tx_errors++;
	}

	if(inta & ISR_THPDOK){ //High priority tx ok
		priv->link_detect.NumTxOkInPeriod++; //YJ,add,080828
		priv->stats.txhpokint++;
		rtl8180_tx_isr(dev,HI_PRIORITY,0);
	}

	if(inta & ISR_RER) {
		priv->stats.rxerr++;
	}
	if(inta & ISR_TBKDER){ //corresponding to BK_PRIORITY
		priv->stats.txbkperr++;
		priv->ieee80211->stats.tx_errors++;
		rtl8180_tx_isr(dev,BK_PRIORITY,1);
		rtl8180_try_wake_queue(dev, BE_PRIORITY);
	}

	if(inta & ISR_TBEDER){ //corresponding to BE_PRIORITY
		priv->stats.txbeperr++;
		priv->ieee80211->stats.tx_errors++;
		rtl8180_tx_isr(dev,BE_PRIORITY,1);
		rtl8180_try_wake_queue(dev, BE_PRIORITY);
	}
	if(inta & ISR_TNPDER){ //corresponding to VO_PRIORITY
		priv->stats.txnperr++;
		priv->ieee80211->stats.tx_errors++;
		rtl8180_tx_isr(dev,NORM_PRIORITY,1);
		rtl8180_try_wake_queue(dev, NORM_PRIORITY);
	}

	if(inta & ISR_TLPDER){ //corresponding to VI_PRIORITY
		priv->stats.txlperr++;
		priv->ieee80211->stats.tx_errors++;
		rtl8180_tx_isr(dev,LOW_PRIORITY,1);
		rtl8180_try_wake_queue(dev, LOW_PRIORITY);
	}

	if(inta & ISR_ROK){
		priv->stats.rxint++;
		tasklet_schedule(&priv->irq_rx_tasklet);
	}

	if(inta & ISR_RQoSOK ){
		priv->stats.rxint++;
		tasklet_schedule(&priv->irq_rx_tasklet);
	}
	if(inta & ISR_BcnInt) {
		rtl8180_prepare_beacon(dev);
	}

	if(inta & ISR_RDU){
		DMESGW("No RX descriptor available");
		priv->stats.rxrdu++;
		tasklet_schedule(&priv->irq_rx_tasklet);
	}

	if(inta & ISR_RXFOVW){
		priv->stats.rxoverflow++;
		tasklet_schedule(&priv->irq_rx_tasklet);
	}

	if (inta & ISR_TXFOVW)
		priv->stats.txoverflow++;

	if(inta & ISR_TNPDOK){ //Normal priority tx ok
		priv->link_detect.NumTxOkInPeriod++; //YJ,add,080828
		priv->stats.txnpokint++;
		rtl8180_tx_isr(dev,NORM_PRIORITY,0);
	}

	if(inta & ISR_TLPDOK){ //Low priority tx ok
		priv->link_detect.NumTxOkInPeriod++; //YJ,add,080828
		priv->stats.txlpokint++;
		rtl8180_tx_isr(dev,LOW_PRIORITY,0);
		rtl8180_try_wake_queue(dev, LOW_PRIORITY);
	}

	if(inta & ISR_TBKDOK){ //corresponding to BK_PRIORITY
		priv->stats.txbkpokint++;
		priv->link_detect.NumTxOkInPeriod++; //YJ,add,080828
		rtl8180_tx_isr(dev,BK_PRIORITY,0);
		rtl8180_try_wake_queue(dev, BE_PRIORITY);
	}

	if(inta & ISR_TBEDOK){ //corresponding to BE_PRIORITY
		priv->stats.txbeperr++;
		priv->link_detect.NumTxOkInPeriod++; //YJ,add,080828
		rtl8180_tx_isr(dev,BE_PRIORITY,0);
		rtl8180_try_wake_queue(dev, BE_PRIORITY);
	}
	force_pci_posting(dev);
	spin_unlock_irqrestore(&priv->irq_th_lock,flags);

	return IRQ_HANDLED;
}

void rtl8180_irq_rx_tasklet(struct r8180_priv* priv)
{
	rtl8180_rx(priv->dev);
}

void GPIOChangeRFWorkItemCallBack(struct work_struct *work)
{
	struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, GPIOChangeRFWorkItem.work);
	struct net_device *dev = ieee->dev;
	struct r8180_priv *priv = ieee80211_priv(dev);
	u8 btPSR;
	u8 btConfig0;
	RT_RF_POWER_STATE	eRfPowerStateToSet;
	bool 	bActuallySet=false;

	char *argv[3];
        static char *RadioPowerPath = "/etc/acpi/events/RadioPower.sh";
        static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL};
	static int readf_count = 0;

#ifdef ENABLE_LPS
	if(readf_count % 10 == 0)
		priv->PowerProfile = read_acadapter_file("/proc/acpi/ac_adapter/AC0/state");

	readf_count = (readf_count+1)%0xffff;
#endif
		{
			// We should turn off LED before polling FF51[4].

			//Turn off LED.
			btPSR = read_nic_byte(dev, PSR);
			write_nic_byte(dev, PSR, (btPSR & ~BIT3));

			//It need to delay 4us suggested by Jong, 2008-01-16
			udelay(4);

			//HW radio On/Off according to the value of FF51[4](config0)
			btConfig0 = btPSR = read_nic_byte(dev, CONFIG0);

			//Turn on LED.
			write_nic_byte(dev, PSR, btPSR| BIT3);

			eRfPowerStateToSet = (btConfig0 & BIT4) ?  eRfOn : eRfOff;

			if((priv->ieee80211->bHwRadioOff == true) && (eRfPowerStateToSet == eRfOn))
			{
				priv->ieee80211->bHwRadioOff = false;
				bActuallySet = true;
			}
			else if((priv->ieee80211->bHwRadioOff == false) && (eRfPowerStateToSet == eRfOff))
			{
				priv->ieee80211->bHwRadioOff = true;
				bActuallySet = true;
			}

			if(bActuallySet)
			{
				MgntActSet_RF_State(dev, eRfPowerStateToSet, RF_CHANGE_BY_HW);

				/* To update the UI status for Power status changed */
                                if(priv->ieee80211->bHwRadioOff == true)
                                        argv[1] = "RFOFF";
                                else{
                                        //if(!priv->RfOffReason)
                                                argv[1] = "RFON";
                                        //else
                                        //      argv[1] = "RFOFF";
                                }
                                argv[0] = RadioPowerPath;
                                argv[2] = NULL;

                                call_usermodehelper(RadioPowerPath,argv,envp,1);
			}
		}
}

static u8 read_acadapter_file(char *filename)
{
	return 0;
}

module_init(rtl8180_pci_module_init);
module_exit(rtl8180_pci_module_exit);
