📄 xmit.c
字号:
/** * Airgo MIMO wireless driver * * Copyright (c) 2007-2008 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"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_2ghz_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);#ifdef CONFIG_AGNX_DEBUG print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, skb->data, skb->len + 8);#endif// 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -