diff options
Diffstat (limited to 'drivers/staging/agnx/xmit.c')
-rw-r--r-- | drivers/staging/agnx/xmit.c | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/drivers/staging/agnx/xmit.c b/drivers/staging/agnx/xmit.c new file mode 100644 index 00000000000..7f01528b8a4 --- /dev/null +++ b/drivers/staging/agnx/xmit.c @@ -0,0 +1,819 @@ +/** + * Airgo MIMO wireless driver + * + * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> + + * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer + * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include "agnx.h" +#include "debug.h" +#include "phy.h" + +unsigned int rx_frame_cnt = 0; +//unsigned int local_tx_sent_cnt = 0; + +static inline void disable_rx_engine(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + iowrite32(0x100, ctl + AGNX_CIR_RXCTL); + /* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */ + ioread32(ctl + AGNX_CIR_RXCTL); +} + +static inline void enable_rx_engine(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + iowrite32(0x80, ctl + AGNX_CIR_RXCTL); + ioread32(ctl + AGNX_CIR_RXCTL); +} + +inline void disable_rx_interrupt(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + disable_rx_engine(priv); + reg = ioread32(ctl + AGNX_CIR_RXCFG); + reg &= ~0x20; + iowrite32(reg, ctl + AGNX_CIR_RXCFG); + ioread32(ctl + AGNX_CIR_RXCFG); +} + +inline void enable_rx_interrupt(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + reg = ioread32(ctl + AGNX_CIR_RXCFG); + reg |= 0x20; + iowrite32(reg, ctl + AGNX_CIR_RXCFG); + ioread32(ctl + AGNX_CIR_RXCFG); + enable_rx_engine(priv); +} + +static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + memset(info, 0, sizeof(*info)); + + info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr); + info->skb = dev_alloc_skb(info->dma_len); + if (info->skb == NULL) + agnx_bug("refill err"); + + info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb), + info->dma_len, PCI_DMA_FROMDEVICE); + memset(desc, 0, sizeof(*desc)); + desc->dma_addr = cpu_to_be32(info->mapping); + /* Set the owner to the card */ + desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); +} + +static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_info *info = priv->rx.info + idx; + + /* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */ + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); + rx_desc_init(priv, idx); +} + +static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + memset(desc, 0, sizeof(*desc)); + desc->dma_addr = cpu_to_be32(info->mapping); + /* Set the owner to the card */ + desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); +} + +static void rx_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + BUG_ON(!desc || !info); + if (info->mapping) + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); + if (info->skb) + dev_kfree_skb(info->skb); + memset(info, 0, sizeof(*info)); + memset(desc, 0, sizeof(*desc)); +} + +static inline void __tx_desc_free(struct agnx_priv *priv, + struct agnx_desc *desc, struct agnx_info *info) +{ + BUG_ON(!desc || !info); + /* TODO make sure mapping, skb and len are consistency */ + if (info->mapping) + pci_unmap_single(priv->pdev, info->mapping, + info->dma_len, PCI_DMA_TODEVICE); + if (info->type == PACKET) + dev_kfree_skb(info->skb); + + memset(info, 0, sizeof(*info)); + memset(desc, 0, sizeof(*desc)); +} + +static void txm_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->txm.desc + idx; + struct agnx_info *info = priv->txm.info + idx; + + __tx_desc_free(priv, desc, info); +} + +static void txd_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->txd.desc + idx; + struct agnx_info *info = priv->txd.info + idx; + + __tx_desc_free(priv, desc, info); +} + +int fill_rings(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + unsigned int i; + u32 reg; + AGNX_TRACE; + + priv->txd.idx_sent = priv->txm.idx_sent = 0; + priv->rx.idx = priv->txm.idx = priv->txd.idx = 0; + + for (i = 0; i < priv->rx.size; i++) + rx_desc_init(priv, i); + for (i = 0; i < priv->txm.size; i++) { + memset(priv->txm.desc + i, 0, sizeof(struct agnx_desc)); + memset(priv->txm.info + i, 0, sizeof(struct agnx_info)); + } + for (i = 0; i < priv->txd.size; i++) { + memset(priv->txd.desc + i, 0, sizeof(struct agnx_desc)); + memset(priv->txd.info + i, 0, sizeof(struct agnx_info)); + } + + /* FIXME Set the card RX TXM and TXD address */ + agnx_write32(ctl, AGNX_CIR_RXCMSTART, priv->rx.dma); + agnx_write32(ctl, AGNX_CIR_RXCMEND, priv->txm.dma); + + agnx_write32(ctl, AGNX_CIR_TXMSTART, priv->txm.dma); + agnx_write32(ctl, AGNX_CIR_TXMEND, priv->txd.dma); + + agnx_write32(ctl, AGNX_CIR_TXDSTART, priv->txd.dma); + agnx_write32(ctl, AGNX_CIR_TXDEND, priv->txd.dma + + sizeof(struct agnx_desc) * priv->txd.size); + + /* FIXME Relinquish control of rings to card */ + reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); + reg &= ~0x800; + agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); + return 0; +} /* fill_rings */ + +void unfill_rings(struct agnx_priv *priv) +{ + unsigned long flags; + unsigned int i; + AGNX_TRACE; + + spin_lock_irqsave(&priv->lock, flags); + + for (i = 0; i < priv->rx.size; i++) + rx_desc_free(priv, i); + for (i = 0; i < priv->txm.size; i++) + txm_desc_free(priv, i); + for (i = 0; i < priv->txd.size; i++) + txd_desc_free(priv, i); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/* Extract the bitrate out of a CCK PLCP header. + copy from bcm43xx driver */ +static inline u8 agnx_plcp_get_bitrate_cck(__be32 *phyhdr_11b) +{ + /* FIXME */ + switch (*(u8 *)phyhdr_11b) { + case 0x0A: + return 0; + case 0x14: + return 1; + case 0x37: + return 2; + case 0x6E: + return 3; + } + agnx_bug("Wrong plcp rate"); + return 0; +} + +/* FIXME */ +static inline u8 agnx_plcp_get_bitrate_ofdm(__be32 *phyhdr_11g) +{ + u8 rate = *(u8 *)phyhdr_11g & 0xF; + + printk(PFX "G mode rate is 0x%x\n", rate); + return rate; +} + +/* FIXME */ +static void get_rx_stats(struct agnx_priv *priv, struct agnx_hdr *hdr, + struct ieee80211_rx_status *stat) +{ + void __iomem *ctl = priv->ctl; + u8 *rssi; + u32 noise; + /* FIXME just for test */ + int snr = 40; /* signal-to-noise ratio */ + + memset(stat, 0, sizeof(*stat)); + /* RSSI */ + rssi = (u8 *)&hdr->phy_stats_lo; +// stat->ssi = (rssi[0] + rssi[1] + rssi[2]) / 3; + /* Noise */ + noise = ioread32(ctl + AGNX_GCR_NOISE0); + noise += ioread32(ctl + AGNX_GCR_NOISE1); + noise += ioread32(ctl + AGNX_GCR_NOISE2); + stat->noise = noise / 3; + /* Signal quality */ + //snr = stat->ssi - stat->noise; + if (snr >=0 && snr < 40) + stat->signal = 5 * snr / 2; + else if (snr >= 40) + stat->signal = 100; + else + stat->signal = 0; + + + if (hdr->_11b0 && !hdr->_11g0) { + stat->rate_idx = agnx_plcp_get_bitrate_cck(&hdr->_11b0); + } else if (!hdr->_11b0 && hdr->_11g0) { + printk(PFX "RX: Found G mode packet\n"); + stat->rate_idx = agnx_plcp_get_bitrate_ofdm(&hdr->_11g0); + } else + agnx_bug("Unknown packets type"); + + + stat->band = IEEE80211_BAND_2GHZ; + stat->freq = agnx_channels[priv->channel - 1].center_freq; +// stat->antenna = 3; +// stat->mactime = be32_to_cpu(hdr->time_stamp); +// stat->channel = priv->channel; + +} + +static inline void combine_hdr_frag(struct ieee80211_hdr *ieeehdr, + struct sk_buff *skb) +{ + u16 fctl; + unsigned int hdrlen; + + fctl = le16_to_cpu(ieeehdr->frame_control); + hdrlen = ieee80211_hdrlen(fctl); + /* FIXME */ + if (hdrlen < (2+2+6)/*minimum hdr*/ || + hdrlen > sizeof(struct ieee80211_mgmt)) { + printk(KERN_ERR PFX "hdr len is %d\n", hdrlen); + agnx_bug("Wrong ieee80211 hdr detected"); + } + skb_push(skb, hdrlen); + memcpy(skb->data, ieeehdr, hdrlen); +} /* combine_hdr_frag */ + +static inline int agnx_packet_check(struct agnx_priv *priv, struct agnx_hdr *agnxhdr, + unsigned packet_len) +{ + if (agnx_get_bits(CRC_FAIL, CRC_FAIL_SHIFT, be32_to_cpu(agnxhdr->reg1)) == 1){ + printk(PFX "RX: CRC check fail\n"); + goto drop; + } + if (packet_len > 2048) { + printk(PFX "RX: Too long packet detected\n"); + goto drop; + } + + /* FIXME Just usable for Promious Mode, for Manage mode exclude FCS */ +/* if (packet_len - sizeof(*agnxhdr) < FCS_LEN) { */ +/* printk(PFX "RX: Too short packet detected\n"); */ +/* goto drop; */ +/* } */ + return 0; +drop: + priv->stats.dot11FCSErrorCount++; + return -1; +} + +void handle_rx_irq(struct agnx_priv *priv) +{ + struct ieee80211_rx_status status; + unsigned int len; +// AGNX_TRACE; + + do { + struct agnx_desc *desc; + u32 frag; + struct agnx_info *info; + struct agnx_hdr *hdr; + struct sk_buff *skb; + unsigned int i = priv->rx.idx % priv->rx.size; + + desc = priv->rx.desc + i; + frag = be32_to_cpu(desc->frag); + if (frag & OWNER) + break; + + info = priv->rx.info + i; + skb = info->skb; + hdr = (struct agnx_hdr *)(skb->data); + + len = (frag & PACKET_LEN) >> PACKET_LEN_SHIFT; + if (agnx_packet_check(priv, hdr, len) == -1) { + rx_desc_reusing(priv, i); + continue; + } + skb_put(skb, len); + + do { + u16 fctl; + fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr)->frame_control); + if ((fctl & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_BEACON)// && !(fctl & IEEE80211_STYPE_BEACON)) + dump_ieee80211_hdr((struct ieee80211_hdr *)hdr->mac_hdr, "RX"); + } while (0); + + if (hdr->_11b0 && !hdr->_11g0) { +/* int j; */ +/* u16 fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr) */ +/* ->frame_control); */ +/* if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { */ +/* agnx_print_rx_hdr(hdr); */ +// agnx_print_sta(priv, BSSID_STAID); +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ +/* } */ + + get_rx_stats(priv, hdr, &status); + skb_pull(skb, sizeof(*hdr)); + combine_hdr_frag((struct ieee80211_hdr *)hdr->mac_hdr, skb); + } else if (!hdr->_11b0 && hdr->_11g0) { +// int j; + agnx_print_rx_hdr(hdr); + agnx_print_sta(priv, BSSID_STAID); +// for (j = 0; j < 8; j++) + agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); + + print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, + skb->data, skb->len + 8); + +// if (agnx_plcp_get_bitrate_ofdm(&hdr->_11g0) == 0) + get_rx_stats(priv, hdr, &status); + skb_pull(skb, sizeof(*hdr)); + combine_hdr_frag((struct ieee80211_hdr *) + ((void *)&hdr->mac_hdr), skb); +// dump_ieee80211_hdr((struct ieee80211_hdr *)skb->data, "RX G"); + } else + agnx_bug("Unknown packets type"); + ieee80211_rx_irqsafe(priv->hw, skb, &status); + rx_desc_reinit(priv, i); + + } while ( priv->rx.idx++ ); +} /* handle_rx_irq */ + +static inline void handle_tx_irq(struct agnx_priv *priv, struct agnx_ring *ring) +{ + struct agnx_desc *desc; + struct agnx_info *info; + unsigned int idx; + + for (idx = ring->idx_sent; idx < ring->idx; idx++) { + unsigned int i = idx % ring->size; + u32 frag; + + desc = ring->desc + i; + info = ring->info + i; + + frag = be32_to_cpu(desc->frag); + if (frag & OWNER) { + if (info->type == HEADER) + break; + else + agnx_bug("TX error"); + } + + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_TODEVICE); + + do { +// int j; + size_t len; + len = info->skb->len - sizeof(struct agnx_hdr) + info->hdr_len; + // if (len == 614) { +// agnx_print_desc(desc); + if (info->type == PACKET) { +// agnx_print_tx_hdr((struct agnx_hdr *)info->skb->data); +/* agnx_print_sta_power(priv, LOCAL_STAID); */ +/* agnx_print_sta(priv, LOCAL_STAID); */ +/* // for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, 0); */ +// agnx_print_sta_power(priv, BSSID_STAID); +// agnx_print_sta(priv, BSSID_STAID); +// for (j = 0; j < 8; j++) +// agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); + } +// } + } while (0); + + if (info->type == PACKET) { +// dump_txm_registers(priv); +// dump_rxm_registers(priv); +// dump_bm_registers(priv); +// dump_cir_registers(priv); + } + + if (info->type == PACKET) { +// struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(info->skb); + + skb_pull(info->skb, sizeof(struct agnx_hdr)); + memcpy(skb_push(info->skb, info->hdr_len), &info->hdr, info->hdr_len); + +// dump_ieee80211_hdr((struct ieee80211_hdr *)info->skb->data, "TX_HANDLE"); +/* print_hex_dump_bytes("agnx: TX_HANDLE: ", DUMP_PREFIX_NONE, */ +/* info->skb->data, info->skb->len); */ + + if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) + txi->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(priv->hw, info->skb); + + +/* info->tx_status.queue_number = (ring->size - i) / 2; */ +/* ieee80211_tx_status_irqsafe(priv->hw, info->skb, &(info->tx_status)); */ +/* } else */ +/* dev_kfree_skb_irq(info->skb); */ + } + memset(desc, 0, sizeof(*desc)); + memset(info, 0, sizeof(*info)); + } + + ring->idx_sent = idx; + /* TODO fill the priv->low_level_stats */ + + /* ieee80211_wake_queue(priv->hw, 0); */ +} + +void handle_txm_irq(struct agnx_priv *priv) +{ + handle_tx_irq(priv, &priv->txm); +} + +void handle_txd_irq(struct agnx_priv *priv) +{ + handle_tx_irq(priv, &priv->txd); +} + +void handle_other_irq(struct agnx_priv *priv) +{ +// void __iomem *ctl = priv->ctl; + u32 status = priv->irq_status; + void __iomem *ctl = priv->ctl; + u32 reg; + + if (status & IRQ_TX_BEACON) { + iowrite32(IRQ_TX_BEACON, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: TX Beacon control is 0X%.8X\n", ioread32(ctl + AGNX_TXM_BEACON_CTL)); + printk(PFX "IRQ: TX Beacon rx frame num: %d\n", rx_frame_cnt); + } + if (status & IRQ_TX_RETRY) { + reg = ioread32(ctl + AGNX_TXM_RETRYSTAID); + printk(PFX "IRQ: TX Retry, RETRY STA ID is %x\n", reg); + } + if (status & IRQ_TX_ACTIVITY) + printk(PFX "IRQ: TX Activity\n"); + if (status & IRQ_RX_ACTIVITY) + printk(PFX "IRQ: RX Activity\n"); + if (status & IRQ_RX_X) + printk(PFX "IRQ: RX X\n"); + if (status & IRQ_RX_Y) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_Y; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_Y, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Y\n"); + } + if (status & IRQ_RX_HASHHIT) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_HASHHIT; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_HASHHIT, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Hash Hit\n"); + + } + if (status & IRQ_RX_FRAME) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_FRAME; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_FRAME, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Frame\n"); + rx_frame_cnt++; + } + if (status & IRQ_ERR_INT) { + iowrite32(IRQ_ERR_INT, ctl + AGNX_INT_STAT); +// agnx_hw_reset(priv); + printk(PFX "IRQ: Error Interrupt\n"); + } + if (status & IRQ_TX_QUE_FULL) + printk(PFX "IRQ: TX Workqueue Full\n"); + if (status & IRQ_BANDMAN_ERR) + printk(PFX "IRQ: Bandwidth Management Error\n"); + if (status & IRQ_TX_DISABLE) + printk(PFX "IRQ: TX Disable\n"); + if (status & IRQ_RX_IVASESKEY) + printk(PFX "IRQ: RX Invalid Session Key\n"); + if (status & IRQ_REP_THHIT) + printk(PFX "IRQ: Replay Threshold Hit\n"); + if (status & IRQ_TIMER1) + printk(PFX "IRQ: Timer1\n"); + if (status & IRQ_TIMER_CNT) + printk(PFX "IRQ: Timer Count\n"); + if (status & IRQ_PHY_FASTINT) + printk(PFX "IRQ: Phy Fast Interrupt\n"); + if (status & IRQ_PHY_SLOWINT) + printk(PFX "IRQ: Phy Slow Interrupt\n"); + if (status & IRQ_OTHER) + printk(PFX "IRQ: 0x80000000\n"); +} /* handle_other_irq */ + + +static inline void route_flag_set(struct agnx_hdr *txhdr) +{ +// u32 reg = 0; + + /* FIXME */ +/* reg = (0x7 << ROUTE_COMPRESSION_SHIFT) & ROUTE_COMPRESSION; */ +/* txhdr->reg5 = cpu_to_be32(reg); */ + txhdr->reg5 = (0xa << 0x0) | (0x7 << 0x18); +// txhdr->reg5 = cpu_to_be32((0xa << 0x0) | (0x7 << 0x18)); +// txhdr->reg5 = cpu_to_be32(0x7 << 0x0); +} + +/* Return 0 if no match */ +static inline unsigned int get_power_level(unsigned int rate, unsigned int antennas_num) +{ + unsigned int power_level; + + switch (rate) { + case 10: + case 20: + case 55: + case 60: + case 90: + case 120: power_level = 22; break; + case 180: power_level = 19; break; + case 240: power_level = 18; break; + case 360: power_level = 16; break; + case 480: power_level = 15; break; + case 540: power_level = 14; break; + default: + agnx_bug("Error rate setting\n"); + } + + if (power_level && (antennas_num == 2)) + power_level -= 3; + + return power_level; +} + +static inline void fill_agnx_hdr(struct agnx_priv *priv, struct agnx_info *tx_info) +{ + struct agnx_hdr *txhdr = (struct agnx_hdr *)tx_info->skb->data; + size_t len; + u16 fc = le16_to_cpu(*(__le16 *)&tx_info->hdr); + u32 reg; + + memset(txhdr, 0, sizeof(*txhdr)); + +// reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, LOCAL_STAID); + reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, BSSID_STAID); + reg |= agnx_set_bits(WORKQUEUE_ID, WORKQUEUE_ID_SHIFT, 0); + txhdr->reg4 = cpu_to_be32(reg); + + /* Set the Hardware Sequence Number to 1? */ + reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 0); +// reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 1); + reg |= agnx_set_bits(MAC_HDR_LEN, MAC_HDR_LEN_SHIFT, tx_info->hdr_len); + txhdr->reg1 = cpu_to_be32(reg); + /* Set the agnx_hdr's MAC header */ + memcpy(txhdr->mac_hdr, &tx_info->hdr, tx_info->hdr_len); + + reg = agnx_set_bits(ACK, ACK_SHIFT, 1); +// reg = agnx_set_bits(ACK, ACK_SHIFT, 0); + reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 0); +// reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 1); + reg |= agnx_set_bits(RELAY, RELAY_SHIFT, 0); + reg |= agnx_set_bits(TM, TM_SHIFT, 0); + txhdr->reg0 = cpu_to_be32(reg); + + /* Set the long and short retry limits */ + txhdr->tx.short_retry_limit = tx_info->txi->control.rates[0].count; + txhdr->tx.long_retry_limit = tx_info->txi->control.rates[0].count; + + /* FIXME */ + len = tx_info->skb->len - sizeof(*txhdr) + tx_info->hdr_len + FCS_LEN; + if (fc & IEEE80211_FCTL_PROTECTED) + len += 8; + len = 2398; + reg = agnx_set_bits(FRAG_SIZE, FRAG_SIZE_SHIFT, len); + len = tx_info->skb->len - sizeof(*txhdr); + reg |= agnx_set_bits(PAYLOAD_LEN, PAYLOAD_LEN_SHIFT, len); + txhdr->reg3 = cpu_to_be32(reg); + + route_flag_set(txhdr); +} /* fill_hdr */ + +static void txm_power_set(struct agnx_priv *priv, + struct ieee80211_tx_info *txi) +{ + struct agnx_sta_power power; + u32 reg; + + /* FIXME */ + if (txi->control.rates[0].idx < 0) { + /* For B mode Short Preamble */ + reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_SHORT); +// control->tx_rate = -control->tx_rate; + } else + reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211G); +// reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_LONG); + reg |= agnx_set_bits(SIGNAL, SIGNAL_SHIFT, 0xB); + reg |= agnx_set_bits(RATE, RATE_SHIFT, 0xB); +// reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 15); + reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 20); + /* if rate < 11M set it to 0 */ + reg |= agnx_set_bits(NUM_TRANSMITTERS, NUM_TRANSMITTERS_SHIFT, 1); +// reg |= agnx_set_bits(EDCF, EDCF_SHIFT, 1); +// reg |= agnx_set_bits(TIFS, TIFS_SHIFT, 1); + + power.reg = reg; +// power.reg = cpu_to_le32(reg); + +// set_sta_power(priv, &power, LOCAL_STAID); + set_sta_power(priv, &power, BSSID_STAID); +} + +static inline int tx_packet_check(struct sk_buff *skb) +{ + unsigned int ieee_len = ieee80211_get_hdrlen_from_skb(skb); + if (skb->len > 2048) { + printk(KERN_ERR PFX "length is %d\n", skb->len); + agnx_bug("Too long TX skb"); + return -1; + } + /* FIXME */ + if (skb->len == ieee_len) { + printk(PFX "A strange TX packet\n"); + return -1; + /* tx_faile_irqsafe(); */ + } + return 0; +} + +static int __agnx_tx(struct agnx_priv *priv, struct sk_buff *skb, + struct agnx_ring *ring) +{ + struct agnx_desc *hdr_desc, *frag_desc; + struct agnx_info *hdr_info, *frag_info; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&priv->lock, flags); + + /* The RX interrupt need be Disable until this TX packet + is handled in the next tx interrupt */ + disable_rx_interrupt(priv); + + i = ring->idx; + ring->idx += 2; +/* if (priv->txm_idx - priv->txm_idx_sent == AGNX_TXM_RING_SIZE - 2) */ +/* ieee80211_stop_queue(priv->hw, 0); */ + + /* Set agnx header's info and desc */ + i %= ring->size; + hdr_desc = ring->desc + i; + hdr_info = ring->info + i; + hdr_info->hdr_len = ieee80211_get_hdrlen_from_skb(skb); + memcpy(&hdr_info->hdr, skb->data, hdr_info->hdr_len); + + /* Add the agnx header to the front of the SKB */ + skb_push(skb, sizeof(struct agnx_hdr) - hdr_info->hdr_len); + + hdr_info->txi = txi; + hdr_info->dma_len = sizeof(struct agnx_hdr); + hdr_info->skb = skb; + hdr_info->type = HEADER; + fill_agnx_hdr(priv, hdr_info); + hdr_info->mapping = pci_map_single(priv->pdev, skb->data, + hdr_info->dma_len, PCI_DMA_TODEVICE); + do { + u32 frag = 0; + frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 1); + frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 0); + frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); + frag |= agnx_set_bits(FIRST_FRAG_LEN, FIRST_FRAG_LEN_SHIFT, 1); + frag |= agnx_set_bits(OWNER, OWNER_SHIFT, 1); + hdr_desc->frag = cpu_to_be32(frag); + } while (0); + hdr_desc->dma_addr = cpu_to_be32(hdr_info->mapping); + + + /* Set Frag's info and desc */ + i = (i + 1) % ring->size; + frag_desc = ring->desc + i; + frag_info = ring->info + i; + memcpy(frag_info, hdr_info, sizeof(struct agnx_info)); + frag_info->type = PACKET; + frag_info->dma_len = skb->len - hdr_info->dma_len; + frag_info->mapping = pci_map_single(priv->pdev, skb->data + hdr_info->dma_len, + frag_info->dma_len, PCI_DMA_TODEVICE); + do { + u32 frag = 0; + frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 0); + frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 1); + frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); + frag |= agnx_set_bits(SUB_FRAG_LEN, SUB_FRAG_LEN_SHIFT, frag_info->dma_len); + frag_desc->frag = cpu_to_be32(frag); + } while (0); + frag_desc->dma_addr = cpu_to_be32(frag_info->mapping); + + txm_power_set(priv, txi); + +/* do { */ +/* int j; */ +/* size_t len; */ +/* len = skb->len - hdr_info->dma_len + hdr_info->hdr_len; */ +/* // if (len == 614) { */ +/* agnx_print_desc(hdr_desc); */ +/* agnx_print_desc(frag_desc); */ +/* agnx_print_tx_hdr((struct agnx_hdr *)skb->data); */ +/* agnx_print_sta_power(priv, LOCAL_STAID); */ +/* agnx_print_sta(priv, LOCAL_STAID); */ +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, j); */ +/* agnx_print_sta_power(priv, BSSID_STAID); */ +/* agnx_print_sta(priv, BSSID_STAID); */ +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ +/* // } */ +/* } while (0); */ + + spin_unlock_irqrestore(&priv->lock, flags); + + /* FIXME ugly code */ + /* Trigger TXM */ + do { + u32 reg; + reg = (ioread32(priv->ctl + AGNX_CIR_TXMCTL)); + reg |= 0x8; + iowrite32((reg), priv->ctl + AGNX_CIR_TXMCTL); + }while (0); + + /* Trigger TXD */ + do { + u32 reg; + reg = (ioread32(priv->ctl + AGNX_CIR_TXDCTL)); + reg |= 0x8; + iowrite32((reg), priv->ctl + AGNX_CIR_TXDCTL); + }while (0); + + return 0; +} + +int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb) +{ + u16 fctl; + + if (tx_packet_check(skb)) + return 0; + +/* print_hex_dump_bytes("agnx: TX_PACKET: ", DUMP_PREFIX_NONE, */ +/* skb->data, skb->len); */ + + fctl = le16_to_cpu(*((__le16 *)skb->data)); + + if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ) + return __agnx_tx(priv, skb, &priv->txd); + else + return __agnx_tx(priv, skb, &priv->txm); +} |