diff options
Diffstat (limited to 'drivers/staging/vt6655/device_main.c')
-rw-r--r-- | drivers/staging/vt6655/device_main.c | 1863 |
1 files changed, 1863 insertions, 0 deletions
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c new file mode 100644 index 000000000..56c3cf3ba --- /dev/null +++ b/drivers/staging/vt6655/device_main.c @@ -0,0 +1,1863 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. + * All rights reserved. + * + * Purpose: driver entry for initial, open, close, tx and rx. + * + * Author: Lyndon Chen + * + * Date: Jan 8, 2003 + * + * Functions: + * + * vt6655_probe - module initial (insmod) driver entry + * vt6655_remove - module remove entry + * device_free_info - device structure resource free function + * device_print_info - print out resource + * device_rx_srv - rx service function + * device_alloc_rx_buf - rx buffer pre-allocated function + * device_free_rx_buf - free rx buffer function + * device_free_tx_buf - free tx buffer function + * device_init_rd0_ring - initial rd dma0 ring + * device_init_rd1_ring - initial rd dma1 ring + * device_init_td0_ring - initial tx dma0 ring buffer + * device_init_td1_ring - initial tx dma1 ring buffer + * device_init_registers - initial MAC & BBP & RF internal registers. + * device_init_rings - initial tx/rx ring buffer + * device_free_rings - free all allocated ring buffer + * device_tx_srv - tx interrupt service function + * + * Revision History: + */ + +#include <linux/file.h> +#include "device.h" +#include "card.h" +#include "channel.h" +#include "baseband.h" +#include "mac.h" +#include "power.h" +#include "rxtx.h" +#include "dpc.h" +#include "rf.h" +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/slab.h> + +/*--------------------- Static Definitions -------------------------*/ +/* + * Define module options + */ +MODULE_AUTHOR("VIA Networking Technologies, Inc., <lyndonchen@vntek.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VIA Networking Solomon-A/B/G Wireless LAN Adapter Driver"); + +#define DEVICE_PARAM(N, D) + +#define RX_DESC_MIN0 16 +#define RX_DESC_MAX0 128 +#define RX_DESC_DEF0 32 +DEVICE_PARAM(RxDescriptors0, "Number of receive descriptors0"); + +#define RX_DESC_MIN1 16 +#define RX_DESC_MAX1 128 +#define RX_DESC_DEF1 32 +DEVICE_PARAM(RxDescriptors1, "Number of receive descriptors1"); + +#define TX_DESC_MIN0 16 +#define TX_DESC_MAX0 128 +#define TX_DESC_DEF0 32 +DEVICE_PARAM(TxDescriptors0, "Number of transmit descriptors0"); + +#define TX_DESC_MIN1 16 +#define TX_DESC_MAX1 128 +#define TX_DESC_DEF1 64 +DEVICE_PARAM(TxDescriptors1, "Number of transmit descriptors1"); + +#define INT_WORKS_DEF 20 +#define INT_WORKS_MIN 10 +#define INT_WORKS_MAX 64 + +DEVICE_PARAM(int_works, "Number of packets per interrupt services"); + +#define RTS_THRESH_DEF 2347 + +#define FRAG_THRESH_DEF 2346 + +#define SHORT_RETRY_MIN 0 +#define SHORT_RETRY_MAX 31 +#define SHORT_RETRY_DEF 8 + +DEVICE_PARAM(ShortRetryLimit, "Short frame retry limits"); + +#define LONG_RETRY_MIN 0 +#define LONG_RETRY_MAX 15 +#define LONG_RETRY_DEF 4 + +DEVICE_PARAM(LongRetryLimit, "long frame retry limits"); + +/* BasebandType[] baseband type selected + * 0: indicate 802.11a type + * 1: indicate 802.11b type + * 2: indicate 802.11g type + */ +#define BBP_TYPE_MIN 0 +#define BBP_TYPE_MAX 2 +#define BBP_TYPE_DEF 2 + +DEVICE_PARAM(BasebandType, "baseband type"); + +/* + * Static vars definitions + */ +static const struct pci_device_id vt6655_pci_id_table[] = { + { PCI_VDEVICE(VIA, 0x3253) }, + { 0, } +}; + +/*--------------------- Static Functions --------------------------*/ + +static int vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent); +static void device_free_info(struct vnt_private *priv); +static void device_print_info(struct vnt_private *priv); + +static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr); +static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr); + +static int device_init_rd0_ring(struct vnt_private *priv); +static int device_init_rd1_ring(struct vnt_private *priv); +static int device_init_td0_ring(struct vnt_private *priv); +static int device_init_td1_ring(struct vnt_private *priv); + +static int device_rx_srv(struct vnt_private *priv, unsigned int idx); +static int device_tx_srv(struct vnt_private *priv, unsigned int idx); +static bool device_alloc_rx_buf(struct vnt_private *, struct vnt_rx_desc *); +static void device_free_rx_buf(struct vnt_private *priv, + struct vnt_rx_desc *rd); +static void device_init_registers(struct vnt_private *priv); +static void device_free_tx_buf(struct vnt_private *, struct vnt_tx_desc *); +static void device_free_td0_ring(struct vnt_private *priv); +static void device_free_td1_ring(struct vnt_private *priv); +static void device_free_rd0_ring(struct vnt_private *priv); +static void device_free_rd1_ring(struct vnt_private *priv); +static void device_free_rings(struct vnt_private *priv); + +/*--------------------- Export Variables --------------------------*/ + +/*--------------------- Export Functions --------------------------*/ + +static void vt6655_remove(struct pci_dev *pcid) +{ + struct vnt_private *priv = pci_get_drvdata(pcid); + + if (!priv) + return; + device_free_info(priv); +} + +static void device_get_options(struct vnt_private *priv) +{ + struct vnt_options *opts = &priv->opts; + + opts->rx_descs0 = RX_DESC_DEF0; + opts->rx_descs1 = RX_DESC_DEF1; + opts->tx_descs[0] = TX_DESC_DEF0; + opts->tx_descs[1] = TX_DESC_DEF1; + opts->int_works = INT_WORKS_DEF; + + opts->short_retry = SHORT_RETRY_DEF; + opts->long_retry = LONG_RETRY_DEF; + opts->bbp_type = BBP_TYPE_DEF; +} + +static void +device_set_options(struct vnt_private *priv) +{ + priv->byShortRetryLimit = priv->opts.short_retry; + priv->byLongRetryLimit = priv->opts.long_retry; + priv->byBBType = priv->opts.bbp_type; + priv->byPacketType = priv->byBBType; + priv->byAutoFBCtrl = AUTO_FB_0; + priv->bUpdateBBVGA = true; + priv->preamble_type = 0; + + pr_debug(" byShortRetryLimit= %d\n", (int)priv->byShortRetryLimit); + pr_debug(" byLongRetryLimit= %d\n", (int)priv->byLongRetryLimit); + pr_debug(" preamble_type= %d\n", (int)priv->preamble_type); + pr_debug(" byShortPreamble= %d\n", (int)priv->byShortPreamble); + pr_debug(" byBBType= %d\n", (int)priv->byBBType); +} + +static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr) +{ + iowrite8(1, iobase + MAC_REG_PAGE1SEL); + for (int i = 0; i < 6; i++) + iowrite8(mac_addr[i], iobase + MAC_REG_BSSID0 + i); + iowrite8(0, iobase + MAC_REG_PAGE1SEL); +} + +static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr) +{ + iowrite8(1, iobase + MAC_REG_PAGE1SEL); + for (int i = 0; i < 6; i++) + mac_addr[i] = ioread8(iobase + MAC_REG_PAR0 + i); + iowrite8(0, iobase + MAC_REG_PAGE1SEL); +} + +static void vt6655_mac_dma_ctl(void __iomem *iobase, u8 reg_index) +{ + u32 reg_value; + + reg_value = ioread32(iobase + reg_index); + if (reg_value & DMACTL_RUN) + iowrite32(DMACTL_WAKE, iobase + reg_index); + else + iowrite32(DMACTL_RUN, iobase + reg_index); +} + +static void vt6655_mac_set_bits(void __iomem *iobase, u32 mask) +{ + u32 reg_value; + + reg_value = ioread32(iobase + MAC_REG_ENCFG); + reg_value = reg_value | mask; + iowrite32(reg_value, iobase + MAC_REG_ENCFG); +} + +static void vt6655_mac_clear_bits(void __iomem *iobase, u32 mask) +{ + u32 reg_value; + + reg_value = ioread32(iobase + MAC_REG_ENCFG); + reg_value = reg_value & ~mask; + iowrite32(reg_value, iobase + MAC_REG_ENCFG); +} + +static void vt6655_mac_en_protect_md(void __iomem *iobase) +{ + vt6655_mac_set_bits(iobase, ENCFG_PROTECTMD); +} + +static void vt6655_mac_dis_protect_md(void __iomem *iobase) +{ + vt6655_mac_clear_bits(iobase, ENCFG_PROTECTMD); +} + +static void vt6655_mac_en_barker_preamble_md(void __iomem *iobase) +{ + vt6655_mac_set_bits(iobase, ENCFG_BARKERPREAM); +} + +static void vt6655_mac_dis_barker_preamble_md(void __iomem *iobase) +{ + vt6655_mac_clear_bits(iobase, ENCFG_BARKERPREAM); +} + +/* + * Initialisation of MAC & BBP registers + */ + +static void device_init_registers(struct vnt_private *priv) +{ + unsigned long flags; + unsigned int ii; + unsigned char byValue; + unsigned char byCCKPwrdBm = 0; + unsigned char byOFDMPwrdBm = 0; + + MACbShutdown(priv); + bb_software_reset(priv); + + /* Do MACbSoftwareReset in MACvInitialize */ + MACbSoftwareReset(priv); + + priv->bAES = false; + + /* Only used in 11g type, sync with ERP IE */ + priv->bProtectMode = false; + + priv->bNonERPPresent = false; + priv->bBarkerPreambleMd = false; + priv->wCurrentRate = RATE_1M; + priv->byTopOFDMBasicRate = RATE_24M; + priv->byTopCCKBasicRate = RATE_1M; + + /* init MAC */ + MACvInitialize(priv); + + /* Get Local ID */ + priv->local_id = ioread8(priv->port_offset + MAC_REG_LOCALID); + + spin_lock_irqsave(&priv->lock, flags); + + SROMvReadAllContents(priv->port_offset, priv->abyEEPROM); + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Get Channel range */ + priv->byMinChannel = 1; + priv->byMaxChannel = CB_MAX_CHANNEL; + + /* Get Antena */ + byValue = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_ANTENNA); + if (byValue & EEP_ANTINV) + priv->bTxRxAntInv = true; + else + priv->bTxRxAntInv = false; + + byValue &= (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); + /* if not set default is All */ + if (byValue == 0) + byValue = (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); + + if (byValue == (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN)) { + priv->byAntennaCount = 2; + priv->byTxAntennaMode = ANT_B; + priv->dwTxAntennaSel = 1; + priv->dwRxAntennaSel = 1; + + if (priv->bTxRxAntInv) + priv->byRxAntennaMode = ANT_A; + else + priv->byRxAntennaMode = ANT_B; + } else { + priv->byAntennaCount = 1; + priv->dwTxAntennaSel = 0; + priv->dwRxAntennaSel = 0; + + if (byValue & EEP_ANTENNA_AUX) { + priv->byTxAntennaMode = ANT_A; + + if (priv->bTxRxAntInv) + priv->byRxAntennaMode = ANT_B; + else + priv->byRxAntennaMode = ANT_A; + } else { + priv->byTxAntennaMode = ANT_B; + + if (priv->bTxRxAntInv) + priv->byRxAntennaMode = ANT_A; + else + priv->byRxAntennaMode = ANT_B; + } + } + + /* Set initial antenna mode */ + bb_set_tx_antenna_mode(priv, priv->byTxAntennaMode); + bb_set_rx_antenna_mode(priv, priv->byRxAntennaMode); + + /* zonetype initial */ + priv->byOriginalZonetype = priv->abyEEPROM[EEP_OFS_ZONETYPE]; + + if (!priv->bZoneRegExist) + priv->byZoneType = priv->abyEEPROM[EEP_OFS_ZONETYPE]; + + pr_debug("priv->byZoneType = %x\n", priv->byZoneType); + + /* Init RF module */ + RFbInit(priv); + + /* Get Desire Power Value */ + priv->byCurPwr = 0xFF; + priv->byCCKPwr = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_PWR_CCK); + priv->byOFDMPwrG = SROMbyReadEmbedded(priv->port_offset, + EEP_OFS_PWR_OFDMG); + + /* Load power Table */ + for (ii = 0; ii < CB_MAX_CHANNEL_24G; ii++) { + priv->abyCCKPwrTbl[ii + 1] = + SROMbyReadEmbedded(priv->port_offset, + (unsigned char)(ii + EEP_OFS_CCK_PWR_TBL)); + if (priv->abyCCKPwrTbl[ii + 1] == 0) + priv->abyCCKPwrTbl[ii + 1] = priv->byCCKPwr; + + priv->abyOFDMPwrTbl[ii + 1] = + SROMbyReadEmbedded(priv->port_offset, + (unsigned char)(ii + EEP_OFS_OFDM_PWR_TBL)); + if (priv->abyOFDMPwrTbl[ii + 1] == 0) + priv->abyOFDMPwrTbl[ii + 1] = priv->byOFDMPwrG; + + priv->abyCCKDefaultPwr[ii + 1] = byCCKPwrdBm; + priv->abyOFDMDefaultPwr[ii + 1] = byOFDMPwrdBm; + } + + /* recover 12,13 ,14channel for EUROPE by 11 channel */ + for (ii = 11; ii < 14; ii++) { + priv->abyCCKPwrTbl[ii] = priv->abyCCKPwrTbl[10]; + priv->abyOFDMPwrTbl[ii] = priv->abyOFDMPwrTbl[10]; + } + + /* Load OFDM A Power Table */ + for (ii = 0; ii < CB_MAX_CHANNEL_5G; ii++) { + priv->abyOFDMPwrTbl[ii + CB_MAX_CHANNEL_24G + 1] = + SROMbyReadEmbedded(priv->port_offset, + (unsigned char)(ii + EEP_OFS_OFDMA_PWR_TBL)); + + priv->abyOFDMDefaultPwr[ii + CB_MAX_CHANNEL_24G + 1] = + SROMbyReadEmbedded(priv->port_offset, + (unsigned char)(ii + EEP_OFS_OFDMA_PWR_dBm)); + } + + if (priv->local_id > REV_ID_VT3253_B1) { + VT6655_MAC_SELECT_PAGE1(priv->port_offset); + + iowrite8(MSRCTL1_TXPWR | MSRCTL1_CSAPAREN, priv->port_offset + MAC_REG_MSRCTL + 1); + + VT6655_MAC_SELECT_PAGE0(priv->port_offset); + } + + /* use relative tx timeout and 802.11i D4 */ + vt6655_mac_word_reg_bits_on(priv->port_offset, MAC_REG_CFG, + (CFG_TKIPOPT | CFG_NOTXTIMEOUT)); + + /* set performance parameter by registry */ + vt6655_mac_set_short_retry_limit(priv, priv->byShortRetryLimit); + MACvSetLongRetryLimit(priv, priv->byLongRetryLimit); + + /* reset TSF counter */ + iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); + /* enable TSF counter */ + iowrite8(TFTCTL_TSFCNTREN, priv->port_offset + MAC_REG_TFTCTL); + + /* initialize BBP registers */ + bb_vt3253_init(priv); + + if (priv->bUpdateBBVGA) { + priv->byBBVGACurrent = priv->abyBBVGA[0]; + priv->byBBVGANew = priv->byBBVGACurrent; + bb_set_vga_gain_offset(priv, priv->abyBBVGA[0]); + } + + bb_set_rx_antenna_mode(priv, priv->byRxAntennaMode); + bb_set_tx_antenna_mode(priv, priv->byTxAntennaMode); + + /* Set BB and packet type at the same time. */ + /* Set Short Slot Time, xIFS, and RSPINF. */ + priv->wCurrentRate = RATE_54M; + + priv->radio_off = false; + + priv->byRadioCtl = SROMbyReadEmbedded(priv->port_offset, + EEP_OFS_RADIOCTL); + priv->hw_radio_off = false; + + if (priv->byRadioCtl & EEP_RADIOCTL_ENABLE) { + /* Get GPIO */ + priv->byGPIO = ioread8(priv->port_offset + MAC_REG_GPIOCTL1); + + if (((priv->byGPIO & GPIO0_DATA) && + !(priv->byRadioCtl & EEP_RADIOCTL_INV)) || + (!(priv->byGPIO & GPIO0_DATA) && + (priv->byRadioCtl & EEP_RADIOCTL_INV))) + priv->hw_radio_off = true; + } + + if (priv->hw_radio_off || priv->bRadioControlOff) + CARDbRadioPowerOff(priv); + + /* get Permanent network address */ + SROMvReadEtherAddress(priv->port_offset, priv->abyCurrentNetAddr); + pr_debug("Network address = %pM\n", priv->abyCurrentNetAddr); + + /* reset Tx pointer */ + CARDvSafeResetRx(priv); + /* reset Rx pointer */ + CARDvSafeResetTx(priv); + + if (priv->local_id <= REV_ID_VT3253_A1) + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_RCR, RCR_WPAERR); + + /* Turn On Rx DMA */ + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL0); + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL1); + + /* start the adapter */ + iowrite8(HOSTCR_MACEN | HOSTCR_RXON | HOSTCR_TXON, priv->port_offset + MAC_REG_HOSTCR); +} + +static void device_print_info(struct vnt_private *priv) +{ + dev_info(&priv->pcid->dev, "MAC=%pM IO=0x%lx Mem=0x%lx IRQ=%d\n", + priv->abyCurrentNetAddr, (unsigned long)priv->ioaddr, + (unsigned long)priv->port_offset, priv->pcid->irq); +} + +static void device_free_info(struct vnt_private *priv) +{ + if (!priv) + return; + + if (priv->mac_hw) + ieee80211_unregister_hw(priv->hw); + + if (priv->port_offset) + iounmap(priv->port_offset); + + if (priv->pcid) + pci_release_regions(priv->pcid); + + if (priv->hw) + ieee80211_free_hw(priv->hw); +} + +static bool device_init_rings(struct vnt_private *priv) +{ + void *vir_pool; + + /*allocate all RD/TD rings a single pool*/ + vir_pool = dma_alloc_coherent(&priv->pcid->dev, + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + + priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), + &priv->pool_dma, GFP_ATOMIC); + if (!vir_pool) { + dev_err(&priv->pcid->dev, "allocate desc dma memory failed\n"); + return false; + } + + priv->aRD0Ring = vir_pool; + priv->aRD1Ring = vir_pool + + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc); + + priv->rd0_pool_dma = priv->pool_dma; + priv->rd1_pool_dma = priv->rd0_pool_dma + + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc); + + priv->tx0_bufs = dma_alloc_coherent(&priv->pcid->dev, + priv->opts.tx_descs[0] * PKT_BUF_SZ + + priv->opts.tx_descs[1] * PKT_BUF_SZ + + CB_BEACON_BUF_SIZE + + CB_MAX_BUF_SIZE, + &priv->tx_bufs_dma0, GFP_ATOMIC); + if (!priv->tx0_bufs) { + dev_err(&priv->pcid->dev, "allocate buf dma memory failed\n"); + + dma_free_coherent(&priv->pcid->dev, + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + + priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), + vir_pool, priv->pool_dma); + return false; + } + + priv->td0_pool_dma = priv->rd1_pool_dma + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc); + + priv->td1_pool_dma = priv->td0_pool_dma + + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc); + + /* vir_pool: pvoid type */ + priv->apTD0Rings = vir_pool + + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc); + + priv->apTD1Rings = vir_pool + + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc); + + priv->tx1_bufs = priv->tx0_bufs + + priv->opts.tx_descs[0] * PKT_BUF_SZ; + + priv->tx_beacon_bufs = priv->tx1_bufs + + priv->opts.tx_descs[1] * PKT_BUF_SZ; + + priv->pbyTmpBuff = priv->tx_beacon_bufs + + CB_BEACON_BUF_SIZE; + + priv->tx_bufs_dma1 = priv->tx_bufs_dma0 + + priv->opts.tx_descs[0] * PKT_BUF_SZ; + + priv->tx_beacon_dma = priv->tx_bufs_dma1 + + priv->opts.tx_descs[1] * PKT_BUF_SZ; + + return true; +} + +static void device_free_rings(struct vnt_private *priv) +{ + dma_free_coherent(&priv->pcid->dev, + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + + priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), + priv->aRD0Ring, priv->pool_dma); + + dma_free_coherent(&priv->pcid->dev, + priv->opts.tx_descs[0] * PKT_BUF_SZ + + priv->opts.tx_descs[1] * PKT_BUF_SZ + + CB_BEACON_BUF_SIZE + + CB_MAX_BUF_SIZE, + priv->tx0_bufs, priv->tx_bufs_dma0); +} + +static int device_init_rd0_ring(struct vnt_private *priv) +{ + int i; + dma_addr_t curr = priv->rd0_pool_dma; + struct vnt_rx_desc *desc; + int ret; + + /* Init the RD0 ring entries */ + for (i = 0; i < priv->opts.rx_descs0; + i ++, curr += sizeof(struct vnt_rx_desc)) { + desc = &priv->aRD0Ring[i]; + desc->rd_info = kzalloc(sizeof(*desc->rd_info), GFP_KERNEL); + if (!desc->rd_info) { + ret = -ENOMEM; + goto err_free_desc; + } + + if (!device_alloc_rx_buf(priv, desc)) { + dev_err(&priv->pcid->dev, "can not alloc rx bufs\n"); + ret = -ENOMEM; + goto err_free_rd; + } + + desc->next = &priv->aRD0Ring[(i + 1) % priv->opts.rx_descs0]; + desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc)); + } + + if (i > 0) + priv->aRD0Ring[i - 1].next_desc = cpu_to_le32(priv->rd0_pool_dma); + priv->pCurrRD[0] = &priv->aRD0Ring[0]; + + return 0; + +err_free_rd: + kfree(desc->rd_info); + +err_free_desc: + while (i--) { + desc = &priv->aRD0Ring[i]; + device_free_rx_buf(priv, desc); + kfree(desc->rd_info); + } + + return ret; +} + +static int device_init_rd1_ring(struct vnt_private *priv) +{ + int i; + dma_addr_t curr = priv->rd1_pool_dma; + struct vnt_rx_desc *desc; + int ret; + + /* Init the RD1 ring entries */ + for (i = 0; i < priv->opts.rx_descs1; + i ++, curr += sizeof(struct vnt_rx_desc)) { + desc = &priv->aRD1Ring[i]; + desc->rd_info = kzalloc(sizeof(*desc->rd_info), GFP_KERNEL); + if (!desc->rd_info) { + ret = -ENOMEM; + goto err_free_desc; + } + + if (!device_alloc_rx_buf(priv, desc)) { + dev_err(&priv->pcid->dev, "can not alloc rx bufs\n"); + ret = -ENOMEM; + goto err_free_rd; + } + + desc->next = &priv->aRD1Ring[(i + 1) % priv->opts.rx_descs1]; + desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc)); + } + + if (i > 0) + priv->aRD1Ring[i - 1].next_desc = cpu_to_le32(priv->rd1_pool_dma); + priv->pCurrRD[1] = &priv->aRD1Ring[0]; + + return 0; + +err_free_rd: + kfree(desc->rd_info); + +err_free_desc: + while (i--) { + desc = &priv->aRD1Ring[i]; + device_free_rx_buf(priv, desc); + kfree(desc->rd_info); + } + + return ret; +} + +static void device_free_rd0_ring(struct vnt_private *priv) +{ + int i; + + for (i = 0; i < priv->opts.rx_descs0; i++) { + struct vnt_rx_desc *desc = &priv->aRD0Ring[i]; + + device_free_rx_buf(priv, desc); + kfree(desc->rd_info); + } +} + +static void device_free_rd1_ring(struct vnt_private *priv) +{ + int i; + + for (i = 0; i < priv->opts.rx_descs1; i++) { + struct vnt_rx_desc *desc = &priv->aRD1Ring[i]; + + device_free_rx_buf(priv, desc); + kfree(desc->rd_info); + } +} + +static int device_init_td0_ring(struct vnt_private *priv) +{ + int i; + dma_addr_t curr; + struct vnt_tx_desc *desc; + int ret; + + curr = priv->td0_pool_dma; + for (i = 0; i < priv->opts.tx_descs[0]; + i++, curr += sizeof(struct vnt_tx_desc)) { + desc = &priv->apTD0Rings[i]; + desc->td_info = kzalloc(sizeof(*desc->td_info), GFP_KERNEL); + if (!desc->td_info) { + ret = -ENOMEM; + goto err_free_desc; + } + + desc->td_info->buf = priv->tx0_bufs + i * PKT_BUF_SZ; + desc->td_info->buf_dma = priv->tx_bufs_dma0 + i * PKT_BUF_SZ; + + desc->next = &(priv->apTD0Rings[(i + 1) % priv->opts.tx_descs[0]]); + desc->next_desc = cpu_to_le32(curr + + sizeof(struct vnt_tx_desc)); + } + + if (i > 0) + priv->apTD0Rings[i - 1].next_desc = cpu_to_le32(priv->td0_pool_dma); + priv->apTailTD[0] = priv->apCurrTD[0] = &priv->apTD0Rings[0]; + + return 0; + +err_free_desc: + while (i--) { + desc = &priv->apTD0Rings[i]; + kfree(desc->td_info); + } + + return ret; +} + +static int device_init_td1_ring(struct vnt_private *priv) +{ + int i; + dma_addr_t curr; + struct vnt_tx_desc *desc; + int ret; + + /* Init the TD ring entries */ + curr = priv->td1_pool_dma; + for (i = 0; i < priv->opts.tx_descs[1]; + i++, curr += sizeof(struct vnt_tx_desc)) { + desc = &priv->apTD1Rings[i]; + desc->td_info = kzalloc(sizeof(*desc->td_info), GFP_KERNEL); + if (!desc->td_info) { + ret = -ENOMEM; + goto err_free_desc; + } + + desc->td_info->buf = priv->tx1_bufs + i * PKT_BUF_SZ; + desc->td_info->buf_dma = priv->tx_bufs_dma1 + i * PKT_BUF_SZ; + + desc->next = &(priv->apTD1Rings[(i + 1) % priv->opts.tx_descs[1]]); + desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_tx_desc)); + } + + if (i > 0) + priv->apTD1Rings[i - 1].next_desc = cpu_to_le32(priv->td1_pool_dma); + priv->apTailTD[1] = priv->apCurrTD[1] = &priv->apTD1Rings[0]; + + return 0; + +err_free_desc: + while (i--) { + desc = &priv->apTD1Rings[i]; + kfree(desc->td_info); + } + + return ret; +} + +static void device_free_td0_ring(struct vnt_private *priv) +{ + int i; + + for (i = 0; i < priv->opts.tx_descs[0]; i++) { + struct vnt_tx_desc *desc = &priv->apTD0Rings[i]; + struct vnt_td_info *td_info = desc->td_info; + + dev_kfree_skb(td_info->skb); + kfree(desc->td_info); + } +} + +static void device_free_td1_ring(struct vnt_private *priv) +{ + int i; + + for (i = 0; i < priv->opts.tx_descs[1]; i++) { + struct vnt_tx_desc *desc = &priv->apTD1Rings[i]; + struct vnt_td_info *td_info = desc->td_info; + + dev_kfree_skb(td_info->skb); + kfree(desc->td_info); + } +} + +/*-----------------------------------------------------------------*/ + +static int device_rx_srv(struct vnt_private *priv, unsigned int idx) +{ + struct vnt_rx_desc *rd; + int works = 0; + + for (rd = priv->pCurrRD[idx]; + rd->rd0.owner == OWNED_BY_HOST; + rd = rd->next) { + if (works++ > 15) + break; + + if (!rd->rd_info->skb) + break; + + if (vnt_receive_frame(priv, rd)) { + if (!device_alloc_rx_buf(priv, rd)) { + dev_err(&priv->pcid->dev, + "can not allocate rx buf\n"); + break; + } + } + rd->rd0.owner = OWNED_BY_NIC; + } + + priv->pCurrRD[idx] = rd; + + return works; +} + +static bool device_alloc_rx_buf(struct vnt_private *priv, + struct vnt_rx_desc *rd) +{ + struct vnt_rd_info *rd_info = rd->rd_info; + + rd_info->skb = dev_alloc_skb((int)priv->rx_buf_sz); + if (!rd_info->skb) + return false; + + rd_info->skb_dma = + dma_map_single(&priv->pcid->dev, + skb_put(rd_info->skb, skb_tailroom(rd_info->skb)), + priv->rx_buf_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(&priv->pcid->dev, rd_info->skb_dma)) { + dev_kfree_skb(rd_info->skb); + rd_info->skb = NULL; + return false; + } + + *((unsigned int *)&rd->rd0) = 0; /* FIX cast */ + + rd->rd0.res_count = cpu_to_le16(priv->rx_buf_sz); + rd->rd0.owner = OWNED_BY_NIC; + rd->rd1.req_count = cpu_to_le16(priv->rx_buf_sz); + rd->buff_addr = cpu_to_le32(rd_info->skb_dma); + + return true; +} + +static void device_free_rx_buf(struct vnt_private *priv, + struct vnt_rx_desc *rd) +{ + struct vnt_rd_info *rd_info = rd->rd_info; + + dma_unmap_single(&priv->pcid->dev, rd_info->skb_dma, + priv->rx_buf_sz, DMA_FROM_DEVICE); + dev_kfree_skb(rd_info->skb); +} + +static const u8 fallback_rate0[5][5] = { + {RATE_18M, RATE_18M, RATE_12M, RATE_12M, RATE_12M}, + {RATE_24M, RATE_24M, RATE_18M, RATE_12M, RATE_12M}, + {RATE_36M, RATE_36M, RATE_24M, RATE_18M, RATE_18M}, + {RATE_48M, RATE_48M, RATE_36M, RATE_24M, RATE_24M}, + {RATE_54M, RATE_54M, RATE_48M, RATE_36M, RATE_36M} +}; + +static const u8 fallback_rate1[5][5] = { + {RATE_18M, RATE_18M, RATE_12M, RATE_6M, RATE_6M}, + {RATE_24M, RATE_24M, RATE_18M, RATE_6M, RATE_6M}, + {RATE_36M, RATE_36M, RATE_24M, RATE_12M, RATE_12M}, + {RATE_48M, RATE_48M, RATE_24M, RATE_12M, RATE_12M}, + {RATE_54M, RATE_54M, RATE_36M, RATE_18M, RATE_18M} +}; + +static int vnt_int_report_rate(struct vnt_private *priv, + struct vnt_td_info *context, u8 tsr0, u8 tsr1) +{ + struct vnt_tx_fifo_head *fifo_head; + struct ieee80211_tx_info *info; + struct ieee80211_rate *rate; + u16 fb_option; + u8 tx_retry = (tsr0 & TSR0_NCR); + s8 idx; + + if (!context) + return -ENOMEM; + + if (!context->skb) + return -EINVAL; + + fifo_head = (struct vnt_tx_fifo_head *)context->buf; + fb_option = (le16_to_cpu(fifo_head->fifo_ctl) & + (FIFOCTL_AUTO_FB_0 | FIFOCTL_AUTO_FB_1)); + + info = IEEE80211_SKB_CB(context->skb); + idx = info->control.rates[0].idx; + + if (fb_option && !(tsr1 & TSR1_TERR)) { + u8 tx_rate; + u8 retry = tx_retry; + + rate = ieee80211_get_tx_rate(priv->hw, info); + tx_rate = rate->hw_value - RATE_18M; + + if (retry > 4) + retry = 4; + + if (fb_option & FIFOCTL_AUTO_FB_0) + tx_rate = fallback_rate0[tx_rate][retry]; + else if (fb_option & FIFOCTL_AUTO_FB_1) + tx_rate = fallback_rate1[tx_rate][retry]; + + if (info->band == NL80211_BAND_5GHZ) + idx = tx_rate - RATE_6M; + else + idx = tx_rate; + } + + ieee80211_tx_info_clear_status(info); + + info->status.rates[0].count = tx_retry; + + if (!(tsr1 & TSR1_TERR)) { + info->status.rates[0].idx = idx; + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + info->flags |= IEEE80211_TX_STAT_ACK; + } + + return 0; +} + +static int device_tx_srv(struct vnt_private *priv, unsigned int idx) +{ + struct vnt_tx_desc *desc; + int works = 0; + unsigned char byTsr0; + unsigned char byTsr1; + + for (desc = priv->apTailTD[idx]; priv->iTDUsed[idx] > 0; desc = desc->next) { + if (desc->td0.owner == OWNED_BY_NIC) + break; + if (works++ > 15) + break; + + byTsr0 = desc->td0.tsr0; + byTsr1 = desc->td0.tsr1; + + /* Only the status of first TD in the chain is correct */ + if (desc->td1.tcr & TCR_STP) { + if ((desc->td_info->flags & TD_FLAGS_NETIF_SKB) != 0) { + if (!(byTsr1 & TSR1_TERR)) { + if (byTsr0 != 0) { + pr_debug(" Tx[%d] OK but has error. tsr1[%02X] tsr0[%02X]\n", + (int)idx, byTsr1, + byTsr0); + } + } else { + pr_debug(" Tx[%d] dropped & tsr1[%02X] tsr0[%02X]\n", + (int)idx, byTsr1, byTsr0); + } + } + + if (byTsr1 & TSR1_TERR) { + if ((desc->td_info->flags & TD_FLAGS_PRIV_SKB) != 0) { + pr_debug(" Tx[%d] fail has error. tsr1[%02X] tsr0[%02X]\n", + (int)idx, byTsr1, byTsr0); + } + } + + vnt_int_report_rate(priv, desc->td_info, byTsr0, byTsr1); + + device_free_tx_buf(priv, desc); + priv->iTDUsed[idx]--; + } + } + + priv->apTailTD[idx] = desc; + + return works; +} + +static void device_error(struct vnt_private *priv, unsigned short status) +{ + if (status & ISR_FETALERR) { + dev_err(&priv->pcid->dev, "Hardware fatal error\n"); + + MACbShutdown(priv); + return; + } +} + +static void device_free_tx_buf(struct vnt_private *priv, + struct vnt_tx_desc *desc) +{ + struct vnt_td_info *td_info = desc->td_info; + struct sk_buff *skb = td_info->skb; + + if (skb) + ieee80211_tx_status_irqsafe(priv->hw, skb); + + td_info->skb = NULL; + td_info->flags = 0; +} + +static void vnt_check_bb_vga(struct vnt_private *priv) +{ + long dbm; + int i; + + if (!priv->bUpdateBBVGA) + return; + + if (priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + return; + + if (!(priv->vif->cfg.assoc && priv->current_rssi)) + return; + + RFvRSSITodBm(priv, (u8)priv->current_rssi, &dbm); + + for (i = 0; i < BB_VGA_LEVEL; i++) { + if (dbm < priv->dbm_threshold[i]) { + priv->byBBVGANew = priv->abyBBVGA[i]; + break; + } + } + + if (priv->byBBVGANew == priv->byBBVGACurrent) { + priv->uBBVGADiffCount = 1; + return; + } + + priv->uBBVGADiffCount++; + + if (priv->uBBVGADiffCount == 1) { + /* first VGA diff gain */ + bb_set_vga_gain_offset(priv, priv->byBBVGANew); + + dev_dbg(&priv->pcid->dev, + "First RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n", + (int)dbm, priv->byBBVGANew, + priv->byBBVGACurrent, + (int)priv->uBBVGADiffCount); + } + + if (priv->uBBVGADiffCount >= BB_VGA_CHANGE_THRESHOLD) { + dev_dbg(&priv->pcid->dev, + "RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n", + (int)dbm, priv->byBBVGANew, + priv->byBBVGACurrent, + (int)priv->uBBVGADiffCount); + + bb_set_vga_gain_offset(priv, priv->byBBVGANew); + } +} + +static void vnt_interrupt_process(struct vnt_private *priv) +{ + struct ieee80211_low_level_stats *low_stats = &priv->low_stats; + int max_count = 0; + u32 mib_counter; + u32 isr; + unsigned long flags; + + isr = ioread32(priv->port_offset + MAC_REG_ISR); + + if (isr == 0) + return; + + if (isr == 0xffffffff) { + pr_debug("isr = 0xffff\n"); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + /* Read low level stats */ + mib_counter = ioread32(priv->port_offset + MAC_REG_MIBCNTR); + + low_stats->dot11RTSSuccessCount += mib_counter & 0xff; + low_stats->dot11RTSFailureCount += (mib_counter >> 8) & 0xff; + low_stats->dot11ACKFailureCount += (mib_counter >> 16) & 0xff; + low_stats->dot11FCSErrorCount += (mib_counter >> 24) & 0xff; + + /* + * TBD.... + * Must do this after doing rx/tx, cause ISR bit is slow + * than RD/TD write back + * update ISR counter + */ + while (isr && priv->vif) { + iowrite32(isr, priv->port_offset + MAC_REG_ISR); + + if (isr & ISR_FETALERR) { + pr_debug(" ISR_FETALERR\n"); + iowrite8(0, priv->port_offset + MAC_REG_SOFTPWRCTL); + iowrite16(SOFTPWRCTL_SWPECTI, priv->port_offset + MAC_REG_SOFTPWRCTL); + device_error(priv, isr); + } + + if (isr & ISR_TBTT) { + if (priv->op_mode != NL80211_IFTYPE_ADHOC) + vnt_check_bb_vga(priv); + + priv->bBeaconSent = false; + if (priv->bEnablePSMode) + PSbIsNextTBTTWakeUp((void *)priv); + + if ((priv->op_mode == NL80211_IFTYPE_AP || + priv->op_mode == NL80211_IFTYPE_ADHOC) && + priv->vif->bss_conf.enable_beacon) + MACvOneShotTimer1MicroSec(priv, + (priv->vif->bss_conf.beacon_int - + MAKE_BEACON_RESERVED) << 10); + + /* TODO: adhoc PS mode */ + } + + if (isr & ISR_BNTX) { + if (priv->op_mode == NL80211_IFTYPE_ADHOC) { + priv->bIsBeaconBufReadySet = false; + priv->cbBeaconBufReadySetCnt = 0; + } + + priv->bBeaconSent = true; + } + + if (isr & ISR_RXDMA0) + max_count += device_rx_srv(priv, TYPE_RXDMA0); + + if (isr & ISR_RXDMA1) + max_count += device_rx_srv(priv, TYPE_RXDMA1); + + if (isr & ISR_TXDMA0) + max_count += device_tx_srv(priv, TYPE_TXDMA0); + + if (isr & ISR_AC0DMA) + max_count += device_tx_srv(priv, TYPE_AC0DMA); + + if (isr & ISR_SOFTTIMER1) { + if (priv->vif->bss_conf.enable_beacon) + vnt_beacon_make(priv, priv->vif); + } + + /* If both buffers available wake the queue */ + if (AVAIL_TD(priv, TYPE_TXDMA0) && + AVAIL_TD(priv, TYPE_AC0DMA) && + ieee80211_queue_stopped(priv->hw, 0)) + ieee80211_wake_queues(priv->hw); + + isr = ioread32(priv->port_offset + MAC_REG_ISR); + + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL0); + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL1); + + if (max_count > priv->opts.int_works) + break; + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void vnt_interrupt_work(struct work_struct *work) +{ + struct vnt_private *priv = + container_of(work, struct vnt_private, interrupt_work); + + if (priv->vif) + vnt_interrupt_process(priv); + + iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); +} + +static irqreturn_t vnt_interrupt(int irq, void *arg) +{ + struct vnt_private *priv = arg; + + schedule_work(&priv->interrupt_work); + + iowrite32(0, priv->port_offset + MAC_REG_IMR); + + return IRQ_HANDLED; +} + +static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct vnt_tx_desc *head_td; + u32 dma_idx; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (ieee80211_is_data(hdr->frame_control)) + dma_idx = TYPE_AC0DMA; + else + dma_idx = TYPE_TXDMA0; + + if (AVAIL_TD(priv, dma_idx) < 1) { + spin_unlock_irqrestore(&priv->lock, flags); + ieee80211_stop_queues(priv->hw); + return -ENOMEM; + } + + head_td = priv->apCurrTD[dma_idx]; + + head_td->td1.tcr = 0; + + head_td->td_info->skb = skb; + + if (dma_idx == TYPE_AC0DMA) + head_td->td_info->flags = TD_FLAGS_NETIF_SKB; + + priv->apCurrTD[dma_idx] = head_td->next; + + spin_unlock_irqrestore(&priv->lock, flags); + + vnt_generate_fifo_header(priv, dma_idx, head_td, skb); + + spin_lock_irqsave(&priv->lock, flags); + + priv->bPWBitOn = false; + + /* Set TSR1 & ReqCount in TxDescHead */ + head_td->td1.tcr |= (TCR_STP | TCR_EDP | EDMSDU); + head_td->td1.req_count = cpu_to_le16(head_td->td_info->req_count); + + head_td->buff_addr = cpu_to_le32(head_td->td_info->buf_dma); + + /* Poll Transmit the adapter */ + wmb(); + head_td->td0.owner = OWNED_BY_NIC; + wmb(); /* second memory barrier */ + + if (head_td->td_info->flags & TD_FLAGS_NETIF_SKB) + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_AC0DMACTL); + else + vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_TXDMACTL0); + + priv->iTDUsed[dma_idx]++; + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void vnt_tx_80211(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct vnt_private *priv = hw->priv; + + if (vnt_tx_packet(priv, skb)) + ieee80211_free_txskb(hw, skb); +} + +static int vnt_start(struct ieee80211_hw *hw) +{ + struct vnt_private *priv = hw->priv; + int ret; + + priv->rx_buf_sz = PKT_BUF_SZ; + if (!device_init_rings(priv)) + return -ENOMEM; + + ret = request_irq(priv->pcid->irq, vnt_interrupt, + IRQF_SHARED, "vt6655", priv); + if (ret) { + dev_dbg(&priv->pcid->dev, "failed to start irq\n"); + goto err_free_rings; + } + + dev_dbg(&priv->pcid->dev, "call device init rd0 ring\n"); + ret = device_init_rd0_ring(priv); + if (ret) + goto err_free_irq; + ret = device_init_rd1_ring(priv); + if (ret) + goto err_free_rd0_ring; + ret = device_init_td0_ring(priv); + if (ret) + goto err_free_rd1_ring; + ret = device_init_td1_ring(priv); + if (ret) + goto err_free_td0_ring; + + device_init_registers(priv); + + dev_dbg(&priv->pcid->dev, "enable MAC interrupt\n"); + iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); + + ieee80211_wake_queues(hw); + + return 0; + +err_free_td0_ring: + device_free_td0_ring(priv); +err_free_rd1_ring: + device_free_rd1_ring(priv); +err_free_rd0_ring: + device_free_rd0_ring(priv); +err_free_irq: + free_irq(priv->pcid->irq, priv); +err_free_rings: + device_free_rings(priv); + return ret; +} + +static void vnt_stop(struct ieee80211_hw *hw) +{ + struct vnt_private *priv = hw->priv; + + ieee80211_stop_queues(hw); + + cancel_work_sync(&priv->interrupt_work); + + MACbShutdown(priv); + MACbSoftwareReset(priv); + CARDbRadioPowerOff(priv); + + device_free_td0_ring(priv); + device_free_td1_ring(priv); + device_free_rd0_ring(priv); + device_free_rd1_ring(priv); + device_free_rings(priv); + + free_irq(priv->pcid->irq, priv); +} + +static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct vnt_private *priv = hw->priv; + + priv->vif = vif; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_ADHOC: + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); + + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); + + break; + case NL80211_IFTYPE_AP: + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); + + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); + + break; + default: + return -EOPNOTSUPP; + } + + priv->op_mode = vif->type; + + return 0; +} + +static void vnt_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct vnt_private *priv = hw->priv; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_ADHOC: + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(priv->port_offset, + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); + break; + case NL80211_IFTYPE_AP: + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(priv->port_offset, + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); + break; + default: + break; + } + + priv->op_mode = NL80211_IFTYPE_UNSPECIFIED; +} + +static int vnt_config(struct ieee80211_hw *hw, u32 changed) +{ + struct vnt_private *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + u8 bb_type; + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (conf->flags & IEEE80211_CONF_PS) + PSvEnablePowerSaving(priv, conf->listen_interval); + else + PSvDisablePowerSaving(priv); + } + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || + (conf->flags & IEEE80211_CONF_OFFCHANNEL)) { + set_channel(priv, conf->chandef.chan); + + if (conf->chandef.chan->band == NL80211_BAND_5GHZ) + bb_type = BB_TYPE_11A; + else + bb_type = BB_TYPE_11G; + + if (priv->byBBType != bb_type) { + priv->byBBType = bb_type; + + CARDbSetPhyParameter(priv, priv->byBBType); + } + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + if (priv->byBBType == BB_TYPE_11B) + priv->wCurrentRate = RATE_1M; + else + priv->wCurrentRate = RATE_54M; + + RFbSetPower(priv, priv->wCurrentRate, + conf->chandef.chan->hw_value); + } + + return 0; +} + +static void vnt_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, u64 changed) +{ + struct vnt_private *priv = hw->priv; + + priv->current_aid = vif->cfg.aid; + + if (changed & BSS_CHANGED_BSSID && conf->bssid) { + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + vt6655_mac_write_bssid_addr(priv->port_offset, conf->bssid); + + spin_unlock_irqrestore(&priv->lock, flags); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + priv->basic_rates = conf->basic_rates; + + CARDvUpdateBasicTopRate(priv); + + dev_dbg(&priv->pcid->dev, + "basic rates %x\n", conf->basic_rates); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + if (conf->use_short_preamble) { + vt6655_mac_en_barker_preamble_md(priv->port_offset); + priv->preamble_type = true; + } else { + vt6655_mac_dis_barker_preamble_md(priv->port_offset); + priv->preamble_type = false; + } + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + if (conf->use_cts_prot) + vt6655_mac_en_protect_md(priv->port_offset); + else + vt6655_mac_dis_protect_md(priv->port_offset); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + if (conf->use_short_slot) + priv->short_slot_time = true; + else + priv->short_slot_time = false; + + CARDbSetPhyParameter(priv, priv->byBBType); + bb_set_vga_gain_offset(priv, priv->abyBBVGA[0]); + } + + if (changed & BSS_CHANGED_TXPOWER) + RFbSetPower(priv, priv->wCurrentRate, + conf->chandef.chan->hw_value); + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + dev_dbg(&priv->pcid->dev, + "Beacon enable %d\n", conf->enable_beacon); + + if (conf->enable_beacon) { + vnt_beacon_enable(priv, vif, conf); + + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + } else { + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, + TCR_AUTOBCNTX); + } + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INFO) && + priv->op_mode != NL80211_IFTYPE_AP) { + if (vif->cfg.assoc && conf->beacon_rate) { + CARDbUpdateTSF(priv, conf->beacon_rate->hw_value, + conf->sync_tsf); + + CARDbSetBeaconPeriod(priv, conf->beacon_int); + + CARDvSetFirstNextTBTT(priv, conf->beacon_int); + } else { + iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); + iowrite8(TFTCTL_TSFCNTREN, priv->port_offset + MAC_REG_TFTCTL); + } + } +} + +static u64 vnt_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct vnt_private *priv = hw->priv; + struct netdev_hw_addr *ha; + u64 mc_filter = 0; + u32 bit_nr = 0; + + netdev_hw_addr_list_for_each(ha, mc_list) { + bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; + + mc_filter |= 1ULL << (bit_nr & 0x3f); + } + + priv->mc_list_count = mc_list->count; + + return mc_filter; +} + +static void vnt_configure(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct vnt_private *priv = hw->priv; + u8 rx_mode = 0; + + *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; + + rx_mode = ioread8(priv->port_offset + MAC_REG_RCR); + + dev_dbg(&priv->pcid->dev, "rx mode in = %x\n", rx_mode); + + if (changed_flags & FIF_ALLMULTI) { + if (*total_flags & FIF_ALLMULTI) { + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->mc_list_count > 2) { + VT6655_MAC_SELECT_PAGE1(priv->port_offset); + + iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0); + iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0 + 4); + + VT6655_MAC_SELECT_PAGE0(priv->port_offset); + } else { + VT6655_MAC_SELECT_PAGE1(priv->port_offset); + + multicast = le64_to_cpu(multicast); + iowrite32((u32)multicast, priv->port_offset + MAC_REG_MAR0); + iowrite32((u32)(multicast >> 32), + priv->port_offset + MAC_REG_MAR0 + 4); + + VT6655_MAC_SELECT_PAGE0(priv->port_offset); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + rx_mode |= RCR_MULTICAST | RCR_BROADCAST; + } else { + rx_mode &= ~(RCR_MULTICAST | RCR_BROADCAST); + } + } + + if (changed_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) { + rx_mode |= RCR_MULTICAST | RCR_BROADCAST; + + if (*total_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) + rx_mode &= ~RCR_BSSID; + else + rx_mode |= RCR_BSSID; + } + + iowrite8(rx_mode, priv->port_offset + MAC_REG_RCR); + + dev_dbg(&priv->pcid->dev, "rx mode out= %x\n", rx_mode); +} + +static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct vnt_private *priv = hw->priv; + + switch (cmd) { + case SET_KEY: + if (vnt_set_keys(hw, sta, vif, key)) + return -EOPNOTSUPP; + break; + case DISABLE_KEY: + if (test_bit(key->hw_key_idx, &priv->key_entry_inuse)) + clear_bit(key->hw_key_idx, &priv->key_entry_inuse); + break; + default: + break; + } + + return 0; +} + +static int vnt_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct vnt_private *priv = hw->priv; + + memcpy(stats, &priv->low_stats, sizeof(*stats)); + + return 0; +} + +static u64 vnt_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct vnt_private *priv = hw->priv; + u64 tsf; + + tsf = vt6655_get_current_tsf(priv); + + return tsf; +} + +static void vnt_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf) +{ + struct vnt_private *priv = hw->priv; + + CARDvUpdateNextTBTT(priv, tsf, vif->bss_conf.beacon_int); +} + +static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct vnt_private *priv = hw->priv; + + /* reset TSF counter */ + iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); +} + +static const struct ieee80211_ops vnt_mac_ops = { + .tx = vnt_tx_80211, + .start = vnt_start, + .stop = vnt_stop, + .add_interface = vnt_add_interface, + .remove_interface = vnt_remove_interface, + .config = vnt_config, + .bss_info_changed = vnt_bss_info_changed, + .prepare_multicast = vnt_prepare_multicast, + .configure_filter = vnt_configure, + .set_key = vnt_set_key, + .get_stats = vnt_get_stats, + .get_tsf = vnt_get_tsf, + .set_tsf = vnt_set_tsf, + .reset_tsf = vnt_reset_tsf, +}; + +static int vnt_init(struct vnt_private *priv) +{ + SET_IEEE80211_PERM_ADDR(priv->hw, priv->abyCurrentNetAddr); + + vnt_init_bands(priv); + + if (ieee80211_register_hw(priv->hw)) + return -ENODEV; + + priv->mac_hw = true; + + CARDbRadioPowerOff(priv); + + return 0; +} + +static int +vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) +{ + struct vnt_private *priv; + struct ieee80211_hw *hw; + struct wiphy *wiphy; + int rc; + + dev_notice(&pcid->dev, + "%s Ver. %s\n", DEVICE_FULL_DRV_NAM, DEVICE_VERSION); + + dev_notice(&pcid->dev, + "Copyright (c) 2003 VIA Networking Technologies, Inc.\n"); + + hw = ieee80211_alloc_hw(sizeof(*priv), &vnt_mac_ops); + if (!hw) { + dev_err(&pcid->dev, "could not register ieee80211_hw\n"); + return -ENOMEM; + } + + priv = hw->priv; + priv->pcid = pcid; + + spin_lock_init(&priv->lock); + + priv->hw = hw; + + SET_IEEE80211_DEV(priv->hw, &pcid->dev); + + if (pci_enable_device(pcid)) { + device_free_info(priv); + return -ENODEV; + } + + dev_dbg(&pcid->dev, + "Before get pci_info memaddr is %x\n", priv->memaddr); + + pci_set_master(pcid); + + priv->memaddr = pci_resource_start(pcid, 0); + priv->ioaddr = pci_resource_start(pcid, 1); + priv->port_offset = ioremap(priv->memaddr & PCI_BASE_ADDRESS_MEM_MASK, + 256); + if (!priv->port_offset) { + dev_err(&pcid->dev, ": Failed to IO remapping ..\n"); + device_free_info(priv); + return -ENODEV; + } + + rc = pci_request_regions(pcid, DEVICE_NAME); + if (rc) { + dev_err(&pcid->dev, ": Failed to find PCI device\n"); + device_free_info(priv); + return -ENODEV; + } + + if (dma_set_mask(&pcid->dev, DMA_BIT_MASK(32))) { + dev_err(&pcid->dev, ": Failed to set dma 32 bit mask\n"); + device_free_info(priv); + return -ENODEV; + } + + INIT_WORK(&priv->interrupt_work, vnt_interrupt_work); + + /* do reset */ + if (!MACbSoftwareReset(priv)) { + dev_err(&pcid->dev, ": Failed to access MAC hardware..\n"); + device_free_info(priv); + return -ENODEV; + } + /* initial to reload eeprom */ + MACvInitialize(priv); + vt6655_mac_read_ether_addr(priv->port_offset, priv->abyCurrentNetAddr); + + /* Get RFType */ + priv->byRFType = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_RFTYPE); + priv->byRFType &= RF_MASK; + + dev_dbg(&pcid->dev, "RF Type = %x\n", priv->byRFType); + + device_get_options(priv); + device_set_options(priv); + + wiphy = priv->hw->wiphy; + + wiphy->frag_threshold = FRAG_THRESH_DEF; + wiphy->rts_threshold = RTS_THRESH_DEF; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + + ieee80211_hw_set(priv->hw, TIMING_BEACON_ONLY); + ieee80211_hw_set(priv->hw, SIGNAL_DBM); + ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); + ieee80211_hw_set(priv->hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(priv->hw, SUPPORTS_PS); + + priv->hw->max_signal = 100; + + if (vnt_init(priv)) { + device_free_info(priv); + return -ENODEV; + } + + device_print_info(priv); + pci_set_drvdata(pcid, priv); + + return 0; +} + +/*------------------------------------------------------------------*/ + +static int __maybe_unused vt6655_suspend(struct device *dev_d) +{ + struct vnt_private *priv = dev_get_drvdata(dev_d); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + MACbShutdown(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int __maybe_unused vt6655_resume(struct device *dev_d) +{ + device_wakeup_disable(dev_d); + + return 0; +} + +MODULE_DEVICE_TABLE(pci, vt6655_pci_id_table); + +static SIMPLE_DEV_PM_OPS(vt6655_pm_ops, vt6655_suspend, vt6655_resume); + +static struct pci_driver device_driver = { + .name = DEVICE_NAME, + .id_table = vt6655_pci_id_table, + .probe = vt6655_probe, + .remove = vt6655_remove, + .driver.pm = &vt6655_pm_ops, +}; + +module_pci_driver(device_driver); |