| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. |
| * All rights reserved. |
| * |
| * File: device_main.c |
| * |
| * 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: |
| */ |
| #undef __NO_VERSION__ |
| |
| #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 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->byPreambleType = 0; |
| |
| pr_debug(" byShortRetryLimit= %d\n", (int)priv->byShortRetryLimit); |
| pr_debug(" byLongRetryLimit= %d\n", (int)priv->byLongRetryLimit); |
| pr_debug(" byPreambleType= %d\n", (int)priv->byPreambleType); |
| pr_debug(" byShortPreamble= %d\n", (int)priv->byShortPreamble); |
| pr_debug(" byBBType= %d\n", (int)priv->byBBType); |
| } |
| |
| /* |
| * 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); |
| BBvSoftwareReset(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 */ |
| VNSvInPortB(priv->PortOffset + MAC_REG_LOCALID, &priv->byLocalID); |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| SROMvReadAllContents(priv->PortOffset, priv->abyEEPROM); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| /* Get Channel range */ |
| priv->byMinChannel = 1; |
| priv->byMaxChannel = CB_MAX_CHANNEL; |
| |
| /* Get Antena */ |
| byValue = SROMbyReadEmbedded(priv->PortOffset, 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 */ |
| BBvSetTxAntennaMode(priv, priv->byTxAntennaMode); |
| BBvSetRxAntennaMode(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->PortOffset, EEP_OFS_PWR_CCK); |
| priv->byOFDMPwrG = SROMbyReadEmbedded(priv->PortOffset, EEP_OFS_PWR_OFDMG); |
| |
| /* Load power Table */ |
| for (ii = 0; ii < CB_MAX_CHANNEL_24G; ii++) { |
| priv->abyCCKPwrTbl[ii + 1] = |
| SROMbyReadEmbedded(priv->PortOffset, |
| (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->PortOffset, |
| (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->PortOffset, |
| (unsigned char)(ii + EEP_OFS_OFDMA_PWR_TBL)); |
| |
| priv->abyOFDMDefaultPwr[ii + CB_MAX_CHANNEL_24G + 1] = |
| SROMbyReadEmbedded(priv->PortOffset, |
| (unsigned char)(ii + EEP_OFS_OFDMA_PWR_dBm)); |
| } |
| |
| if (priv->byLocalID > REV_ID_VT3253_B1) { |
| MACvSelectPage1(priv->PortOffset); |
| |
| VNSvOutPortB(priv->PortOffset + MAC_REG_MSRCTL + 1, |
| (MSRCTL1_TXPWR | MSRCTL1_CSAPAREN)); |
| |
| MACvSelectPage0(priv->PortOffset); |
| } |
| |
| /* use relative tx timeout and 802.11i D4 */ |
| MACvWordRegBitsOn(priv->PortOffset, |
| MAC_REG_CFG, (CFG_TKIPOPT | CFG_NOTXTIMEOUT)); |
| |
| /* set performance parameter by registry */ |
| MACvSetShortRetryLimit(priv, priv->byShortRetryLimit); |
| MACvSetLongRetryLimit(priv, priv->byLongRetryLimit); |
| |
| /* reset TSF counter */ |
| VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST); |
| /* enable TSF counter */ |
| VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); |
| |
| /* initialize BBP registers */ |
| BBbVT3253Init(priv); |
| |
| if (priv->bUpdateBBVGA) { |
| priv->byBBVGACurrent = priv->abyBBVGA[0]; |
| priv->byBBVGANew = priv->byBBVGACurrent; |
| BBvSetVGAGainOffset(priv, priv->abyBBVGA[0]); |
| } |
| |
| BBvSetRxAntennaMode(priv, priv->byRxAntennaMode); |
| BBvSetTxAntennaMode(priv, priv->byTxAntennaMode); |
| |
| /* Set BB and packet type at the same time. */ |
| /* Set Short Slot Time, xIFS, and RSPINF. */ |
| priv->wCurrentRate = RATE_54M; |
| |
| priv->bRadioOff = false; |
| |
| priv->byRadioCtl = SROMbyReadEmbedded(priv->PortOffset, |
| EEP_OFS_RADIOCTL); |
| priv->bHWRadioOff = false; |
| |
| if (priv->byRadioCtl & EEP_RADIOCTL_ENABLE) { |
| /* Get GPIO */ |
| MACvGPIOIn(priv->PortOffset, &priv->byGPIO); |
| |
| if (((priv->byGPIO & GPIO0_DATA) && |
| !(priv->byRadioCtl & EEP_RADIOCTL_INV)) || |
| (!(priv->byGPIO & GPIO0_DATA) && |
| (priv->byRadioCtl & EEP_RADIOCTL_INV))) |
| priv->bHWRadioOff = true; |
| } |
| |
| if (priv->bHWRadioOff || priv->bRadioControlOff) |
| CARDbRadioPowerOff(priv); |
| |
| /* get Permanent network address */ |
| SROMvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr); |
| pr_debug("Network address = %pM\n", priv->abyCurrentNetAddr); |
| |
| /* reset Tx pointer */ |
| CARDvSafeResetRx(priv); |
| /* reset Rx pointer */ |
| CARDvSafeResetTx(priv); |
| |
| if (priv->byLocalID <= REV_ID_VT3253_A1) |
| MACvRegBitsOn(priv->PortOffset, MAC_REG_RCR, RCR_WPAERR); |
| |
| /* Turn On Rx DMA */ |
| MACvReceive0(priv->PortOffset); |
| MACvReceive1(priv->PortOffset); |
| |
| /* start the adapter */ |
| MACvStart(priv->PortOffset); |
| } |
| |
| 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->PortOffset, 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->PortOffset) |
| iounmap(priv->PortOffset); |
| |
| 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); |
| |
| if (priv->tx0_bufs) |
| 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->bss_conf.assoc && priv->uCurrRSSI)) |
| return; |
| |
| RFvRSSITodBm(priv, (u8)priv->uCurrRSSI, &dbm); |
| |
| for (i = 0; i < BB_VGA_LEVEL; i++) { |
| if (dbm < priv->ldBmThreshold[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 */ |
| BBvSetVGAGainOffset(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); |
| |
| BBvSetVGAGainOffset(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; |
| |
| MACvReadISR(priv->PortOffset, &isr); |
| |
| if (isr == 0) |
| return; |
| |
| if (isr == 0xffffffff) { |
| pr_debug("isr = 0xffff\n"); |
| return; |
| } |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| /* Read low level stats */ |
| MACvReadMIBCounter(priv->PortOffset, &mib_counter); |
| |
| 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) { |
| MACvWriteISR(priv->PortOffset, isr); |
| |
| if (isr & ISR_FETALERR) { |
| pr_debug(" ISR_FETALERR\n"); |
| VNSvOutPortB(priv->PortOffset + MAC_REG_SOFTPWRCTL, 0); |
| VNSvOutPortW(priv->PortOffset + |
| MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPECTI); |
| 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); |
| |
| MACvReadISR(priv->PortOffset, &isr); |
| |
| MACvReceive0(priv->PortOffset); |
| MACvReceive1(priv->PortOffset); |
| |
| 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); |
| |
| MACvIntEnable(priv->PortOffset, IMR_MASK_VALUE); |
| } |
| |
| static irqreturn_t vnt_interrupt(int irq, void *arg) |
| { |
| struct vnt_private *priv = arg; |
| |
| schedule_work(&priv->interrupt_work); |
| |
| MACvIntDisable(priv->PortOffset); |
| |
| 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) |
| MACvTransmitAC0(priv->PortOffset); |
| else |
| MACvTransmit0(priv->PortOffset); |
| |
| 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, "call MACvIntEnable\n"); |
| MACvIntEnable(priv->PortOffset, IMR_MASK_VALUE); |
| |
| 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: |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_RCR, RCR_UNICAST); |
| |
| MACvRegBitsOn(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_ADHOC); |
| |
| break; |
| case NL80211_IFTYPE_AP: |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_RCR, RCR_UNICAST); |
| |
| MACvRegBitsOn(priv->PortOffset, 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: |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX); |
| MACvRegBitsOff(priv->PortOffset, |
| MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_ADHOC); |
| break; |
| case NL80211_IFTYPE_AP: |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX); |
| MACvRegBitsOff(priv->PortOffset, |
| MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); |
| MACvRegBitsOff(priv->PortOffset, 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, u32 changed) |
| { |
| struct vnt_private *priv = hw->priv; |
| |
| priv->current_aid = conf->aid; |
| |
| if (changed & BSS_CHANGED_BSSID && conf->bssid) { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| MACvWriteBSSIDAddress(priv->PortOffset, (u8 *)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) { |
| MACvEnableBarkerPreambleMd(priv->PortOffset); |
| priv->byPreambleType = true; |
| } else { |
| MACvDisableBarkerPreambleMd(priv->PortOffset); |
| priv->byPreambleType = false; |
| } |
| } |
| |
| if (changed & BSS_CHANGED_ERP_CTS_PROT) { |
| if (conf->use_cts_prot) |
| MACvEnableProtectMD(priv->PortOffset); |
| else |
| MACvDisableProtectMD(priv->PortOffset); |
| } |
| |
| if (changed & BSS_CHANGED_ERP_SLOT) { |
| if (conf->use_short_slot) |
| priv->bShortSlotTime = true; |
| else |
| priv->bShortSlotTime = false; |
| |
| CARDbSetPhyParameter(priv, priv->byBBType); |
| BBvSetVGAGainOffset(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); |
| |
| MACvRegBitsOn(priv->PortOffset, MAC_REG_TCR, |
| TCR_AUTOBCNTX); |
| } else { |
| MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, |
| TCR_AUTOBCNTX); |
| } |
| } |
| |
| if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INFO) && |
| priv->op_mode != NL80211_IFTYPE_AP) { |
| if (conf->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 { |
| VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, |
| TFTCTL_TSFCNTRST); |
| VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, |
| TFTCTL_TSFCNTREN); |
| } |
| } |
| } |
| |
| 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; |
| |
| VNSvInPortB(priv->PortOffset + MAC_REG_RCR, &rx_mode); |
| |
| 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) { |
| MACvSelectPage1(priv->PortOffset); |
| |
| VNSvOutPortD(priv->PortOffset + |
| MAC_REG_MAR0, 0xffffffff); |
| VNSvOutPortD(priv->PortOffset + |
| MAC_REG_MAR0 + 4, 0xffffffff); |
| |
| MACvSelectPage0(priv->PortOffset); |
| } else { |
| MACvSelectPage1(priv->PortOffset); |
| |
| VNSvOutPortD(priv->PortOffset + |
| MAC_REG_MAR0, (u32)multicast); |
| VNSvOutPortD(priv->PortOffset + |
| MAC_REG_MAR0 + 4, |
| (u32)(multicast >> 32)); |
| |
| MACvSelectPage0(priv->PortOffset); |
| } |
| |
| 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; |
| } |
| |
| VNSvOutPortB(priv->PortOffset + MAC_REG_RCR, rx_mode); |
| |
| 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); |
| 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; |
| |
| CARDbGetCurrentTSF(priv, &tsf); |
| |
| 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 */ |
| VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST); |
| } |
| |
| 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->PortOffset = ioremap(priv->memaddr & PCI_BASE_ADDRESS_MEM_MASK, |
| 256); |
| if (!priv->PortOffset) { |
| 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); |
| MACvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr); |
| |
| /* Get RFType */ |
| priv->byRFType = SROMbyReadEmbedded(priv->PortOffset, 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)) |
| return -ENODEV; |
| |
| device_print_info(priv); |
| pci_set_drvdata(pcid, priv); |
| |
| return 0; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| |
| #ifdef CONFIG_PM |
| static int vt6655_suspend(struct pci_dev *pcid, pm_message_t state) |
| { |
| struct vnt_private *priv = pci_get_drvdata(pcid); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| pci_save_state(pcid); |
| |
| MACbShutdown(priv); |
| |
| pci_disable_device(pcid); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| pci_set_power_state(pcid, pci_choose_state(pcid, state)); |
| |
| return 0; |
| } |
| |
| static int vt6655_resume(struct pci_dev *pcid) |
| { |
| pci_set_power_state(pcid, PCI_D0); |
| pci_enable_wake(pcid, PCI_D0, 0); |
| pci_restore_state(pcid); |
| |
| return 0; |
| } |
| #endif |
| |
| MODULE_DEVICE_TABLE(pci, vt6655_pci_id_table); |
| |
| static struct pci_driver device_driver = { |
| .name = DEVICE_NAME, |
| .id_table = vt6655_pci_id_table, |
| .probe = vt6655_probe, |
| .remove = vt6655_remove, |
| #ifdef CONFIG_PM |
| .suspend = vt6655_suspend, |
| .resume = vt6655_resume, |
| #endif |
| }; |
| |
| module_pci_driver(device_driver); |