aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/agnx/xmit.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/agnx/xmit.c')
-rw-r--r--drivers/staging/agnx/xmit.c819
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 000000000000..7f01528b8a46
--- /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);
+}