Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 1 | /** |
| 2 | * Airgo MIMO wireless driver |
| 3 | * |
| 4 | * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> |
| 5 | |
| 6 | * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer |
| 7 | * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin |
| 8 | |
| 9 | * This program is free software; you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU General Public License version 2 as |
| 11 | * published by the Free Software Foundation. |
| 12 | */ |
| 13 | |
| 14 | #include <linux/pci.h> |
| 15 | #include <linux/delay.h> |
| 16 | #include "agnx.h" |
| 17 | #include "debug.h" |
| 18 | #include "phy.h" |
| 19 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 20 | unsigned int rx_frame_cnt; |
| 21 | /* unsigned int local_tx_sent_cnt = 0; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 22 | |
| 23 | static inline void disable_rx_engine(struct agnx_priv *priv) |
| 24 | { |
| 25 | void __iomem *ctl = priv->ctl; |
| 26 | iowrite32(0x100, ctl + AGNX_CIR_RXCTL); |
| 27 | /* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */ |
| 28 | ioread32(ctl + AGNX_CIR_RXCTL); |
| 29 | } |
| 30 | |
| 31 | static inline void enable_rx_engine(struct agnx_priv *priv) |
| 32 | { |
| 33 | void __iomem *ctl = priv->ctl; |
| 34 | iowrite32(0x80, ctl + AGNX_CIR_RXCTL); |
| 35 | ioread32(ctl + AGNX_CIR_RXCTL); |
| 36 | } |
| 37 | |
| 38 | inline void disable_rx_interrupt(struct agnx_priv *priv) |
| 39 | { |
| 40 | void __iomem *ctl = priv->ctl; |
| 41 | u32 reg; |
| 42 | |
| 43 | disable_rx_engine(priv); |
| 44 | reg = ioread32(ctl + AGNX_CIR_RXCFG); |
| 45 | reg &= ~0x20; |
| 46 | iowrite32(reg, ctl + AGNX_CIR_RXCFG); |
| 47 | ioread32(ctl + AGNX_CIR_RXCFG); |
| 48 | } |
| 49 | |
| 50 | inline void enable_rx_interrupt(struct agnx_priv *priv) |
| 51 | { |
| 52 | void __iomem *ctl = priv->ctl; |
| 53 | u32 reg; |
| 54 | |
| 55 | reg = ioread32(ctl + AGNX_CIR_RXCFG); |
| 56 | reg |= 0x20; |
| 57 | iowrite32(reg, ctl + AGNX_CIR_RXCFG); |
| 58 | ioread32(ctl + AGNX_CIR_RXCFG); |
| 59 | enable_rx_engine(priv); |
| 60 | } |
| 61 | |
| 62 | static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx) |
| 63 | { |
| 64 | struct agnx_desc *desc = priv->rx.desc + idx; |
| 65 | struct agnx_info *info = priv->rx.info + idx; |
| 66 | |
| 67 | memset(info, 0, sizeof(*info)); |
| 68 | |
| 69 | info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr); |
| 70 | info->skb = dev_alloc_skb(info->dma_len); |
| 71 | if (info->skb == NULL) |
| 72 | agnx_bug("refill err"); |
| 73 | |
| 74 | info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb), |
| 75 | info->dma_len, PCI_DMA_FROMDEVICE); |
| 76 | memset(desc, 0, sizeof(*desc)); |
| 77 | desc->dma_addr = cpu_to_be32(info->mapping); |
| 78 | /* Set the owner to the card */ |
| 79 | desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); |
| 80 | } |
| 81 | |
| 82 | static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx) |
| 83 | { |
| 84 | struct agnx_info *info = priv->rx.info + idx; |
| 85 | |
| 86 | /* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */ |
| 87 | pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); |
| 88 | rx_desc_init(priv, idx); |
| 89 | } |
| 90 | |
| 91 | static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx) |
| 92 | { |
| 93 | struct agnx_desc *desc = priv->rx.desc + idx; |
| 94 | struct agnx_info *info = priv->rx.info + idx; |
| 95 | |
| 96 | memset(desc, 0, sizeof(*desc)); |
| 97 | desc->dma_addr = cpu_to_be32(info->mapping); |
| 98 | /* Set the owner to the card */ |
| 99 | desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); |
| 100 | } |
| 101 | |
| 102 | static void rx_desc_free(struct agnx_priv *priv, unsigned int idx) |
| 103 | { |
| 104 | struct agnx_desc *desc = priv->rx.desc + idx; |
| 105 | struct agnx_info *info = priv->rx.info + idx; |
| 106 | |
| 107 | BUG_ON(!desc || !info); |
| 108 | if (info->mapping) |
| 109 | pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); |
| 110 | if (info->skb) |
| 111 | dev_kfree_skb(info->skb); |
| 112 | memset(info, 0, sizeof(*info)); |
| 113 | memset(desc, 0, sizeof(*desc)); |
| 114 | } |
| 115 | |
| 116 | static inline void __tx_desc_free(struct agnx_priv *priv, |
| 117 | struct agnx_desc *desc, struct agnx_info *info) |
| 118 | { |
| 119 | BUG_ON(!desc || !info); |
| 120 | /* TODO make sure mapping, skb and len are consistency */ |
| 121 | if (info->mapping) |
| 122 | pci_unmap_single(priv->pdev, info->mapping, |
| 123 | info->dma_len, PCI_DMA_TODEVICE); |
| 124 | if (info->type == PACKET) |
| 125 | dev_kfree_skb(info->skb); |
| 126 | |
| 127 | memset(info, 0, sizeof(*info)); |
| 128 | memset(desc, 0, sizeof(*desc)); |
| 129 | } |
| 130 | |
| 131 | static void txm_desc_free(struct agnx_priv *priv, unsigned int idx) |
| 132 | { |
| 133 | struct agnx_desc *desc = priv->txm.desc + idx; |
| 134 | struct agnx_info *info = priv->txm.info + idx; |
| 135 | |
| 136 | __tx_desc_free(priv, desc, info); |
| 137 | } |
| 138 | |
| 139 | static void txd_desc_free(struct agnx_priv *priv, unsigned int idx) |
| 140 | { |
| 141 | struct agnx_desc *desc = priv->txd.desc + idx; |
| 142 | struct agnx_info *info = priv->txd.info + idx; |
| 143 | |
| 144 | __tx_desc_free(priv, desc, info); |
| 145 | } |
| 146 | |
| 147 | int fill_rings(struct agnx_priv *priv) |
| 148 | { |
| 149 | void __iomem *ctl = priv->ctl; |
| 150 | unsigned int i; |
| 151 | u32 reg; |
| 152 | AGNX_TRACE; |
| 153 | |
| 154 | priv->txd.idx_sent = priv->txm.idx_sent = 0; |
| 155 | priv->rx.idx = priv->txm.idx = priv->txd.idx = 0; |
| 156 | |
| 157 | for (i = 0; i < priv->rx.size; i++) |
| 158 | rx_desc_init(priv, i); |
| 159 | for (i = 0; i < priv->txm.size; i++) { |
| 160 | memset(priv->txm.desc + i, 0, sizeof(struct agnx_desc)); |
| 161 | memset(priv->txm.info + i, 0, sizeof(struct agnx_info)); |
| 162 | } |
| 163 | for (i = 0; i < priv->txd.size; i++) { |
| 164 | memset(priv->txd.desc + i, 0, sizeof(struct agnx_desc)); |
| 165 | memset(priv->txd.info + i, 0, sizeof(struct agnx_info)); |
| 166 | } |
| 167 | |
| 168 | /* FIXME Set the card RX TXM and TXD address */ |
| 169 | agnx_write32(ctl, AGNX_CIR_RXCMSTART, priv->rx.dma); |
| 170 | agnx_write32(ctl, AGNX_CIR_RXCMEND, priv->txm.dma); |
| 171 | |
| 172 | agnx_write32(ctl, AGNX_CIR_TXMSTART, priv->txm.dma); |
| 173 | agnx_write32(ctl, AGNX_CIR_TXMEND, priv->txd.dma); |
| 174 | |
| 175 | agnx_write32(ctl, AGNX_CIR_TXDSTART, priv->txd.dma); |
| 176 | agnx_write32(ctl, AGNX_CIR_TXDEND, priv->txd.dma + |
| 177 | sizeof(struct agnx_desc) * priv->txd.size); |
| 178 | |
| 179 | /* FIXME Relinquish control of rings to card */ |
| 180 | reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); |
| 181 | reg &= ~0x800; |
| 182 | agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); |
| 183 | return 0; |
| 184 | } /* fill_rings */ |
| 185 | |
| 186 | void unfill_rings(struct agnx_priv *priv) |
| 187 | { |
| 188 | unsigned long flags; |
| 189 | unsigned int i; |
| 190 | AGNX_TRACE; |
| 191 | |
| 192 | spin_lock_irqsave(&priv->lock, flags); |
| 193 | |
| 194 | for (i = 0; i < priv->rx.size; i++) |
| 195 | rx_desc_free(priv, i); |
| 196 | for (i = 0; i < priv->txm.size; i++) |
| 197 | txm_desc_free(priv, i); |
| 198 | for (i = 0; i < priv->txd.size; i++) |
| 199 | txd_desc_free(priv, i); |
| 200 | |
| 201 | spin_unlock_irqrestore(&priv->lock, flags); |
| 202 | } |
| 203 | |
| 204 | /* Extract the bitrate out of a CCK PLCP header. |
| 205 | copy from bcm43xx driver */ |
| 206 | static inline u8 agnx_plcp_get_bitrate_cck(__be32 *phyhdr_11b) |
| 207 | { |
| 208 | /* FIXME */ |
| 209 | switch (*(u8 *)phyhdr_11b) { |
| 210 | case 0x0A: |
| 211 | return 0; |
| 212 | case 0x14: |
| 213 | return 1; |
| 214 | case 0x37: |
| 215 | return 2; |
| 216 | case 0x6E: |
| 217 | return 3; |
| 218 | } |
| 219 | agnx_bug("Wrong plcp rate"); |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | /* FIXME */ |
| 224 | static inline u8 agnx_plcp_get_bitrate_ofdm(__be32 *phyhdr_11g) |
| 225 | { |
| 226 | u8 rate = *(u8 *)phyhdr_11g & 0xF; |
| 227 | |
| 228 | printk(PFX "G mode rate is 0x%x\n", rate); |
| 229 | return rate; |
| 230 | } |
| 231 | |
| 232 | /* FIXME */ |
| 233 | static void get_rx_stats(struct agnx_priv *priv, struct agnx_hdr *hdr, |
| 234 | struct ieee80211_rx_status *stat) |
| 235 | { |
| 236 | void __iomem *ctl = priv->ctl; |
| 237 | u8 *rssi; |
| 238 | u32 noise; |
| 239 | /* FIXME just for test */ |
| 240 | int snr = 40; /* signal-to-noise ratio */ |
| 241 | |
| 242 | memset(stat, 0, sizeof(*stat)); |
| 243 | /* RSSI */ |
| 244 | rssi = (u8 *)&hdr->phy_stats_lo; |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 245 | /* stat->ssi = (rssi[0] + rssi[1] + rssi[2]) / 3; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 246 | /* Noise */ |
| 247 | noise = ioread32(ctl + AGNX_GCR_NOISE0); |
| 248 | noise += ioread32(ctl + AGNX_GCR_NOISE1); |
| 249 | noise += ioread32(ctl + AGNX_GCR_NOISE2); |
| 250 | stat->noise = noise / 3; |
| 251 | /* Signal quality */ |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 252 | /* snr = stat->ssi - stat->noise; */ |
| 253 | if (snr >= 0 && snr < 40) |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 254 | stat->signal = 5 * snr / 2; |
| 255 | else if (snr >= 40) |
| 256 | stat->signal = 100; |
| 257 | else |
| 258 | stat->signal = 0; |
| 259 | |
| 260 | |
| 261 | if (hdr->_11b0 && !hdr->_11g0) { |
| 262 | stat->rate_idx = agnx_plcp_get_bitrate_cck(&hdr->_11b0); |
| 263 | } else if (!hdr->_11b0 && hdr->_11g0) { |
| 264 | printk(PFX "RX: Found G mode packet\n"); |
| 265 | stat->rate_idx = agnx_plcp_get_bitrate_ofdm(&hdr->_11g0); |
| 266 | } else |
| 267 | agnx_bug("Unknown packets type"); |
| 268 | |
| 269 | |
| 270 | stat->band = IEEE80211_BAND_2GHZ; |
| 271 | stat->freq = agnx_channels[priv->channel - 1].center_freq; |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 272 | /* stat->antenna = 3; |
| 273 | stat->mactime = be32_to_cpu(hdr->time_stamp); |
| 274 | stat->channel = priv->channel; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | static inline void combine_hdr_frag(struct ieee80211_hdr *ieeehdr, |
| 278 | struct sk_buff *skb) |
| 279 | { |
| 280 | u16 fctl; |
| 281 | unsigned int hdrlen; |
| 282 | |
| 283 | fctl = le16_to_cpu(ieeehdr->frame_control); |
| 284 | hdrlen = ieee80211_hdrlen(fctl); |
| 285 | /* FIXME */ |
| 286 | if (hdrlen < (2+2+6)/*minimum hdr*/ || |
| 287 | hdrlen > sizeof(struct ieee80211_mgmt)) { |
| 288 | printk(KERN_ERR PFX "hdr len is %d\n", hdrlen); |
| 289 | agnx_bug("Wrong ieee80211 hdr detected"); |
| 290 | } |
| 291 | skb_push(skb, hdrlen); |
| 292 | memcpy(skb->data, ieeehdr, hdrlen); |
| 293 | } /* combine_hdr_frag */ |
| 294 | |
| 295 | static inline int agnx_packet_check(struct agnx_priv *priv, struct agnx_hdr *agnxhdr, |
| 296 | unsigned packet_len) |
| 297 | { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 298 | if (agnx_get_bits(CRC_FAIL, CRC_FAIL_SHIFT, be32_to_cpu(agnxhdr->reg1)) == 1) { |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 299 | printk(PFX "RX: CRC check fail\n"); |
| 300 | goto drop; |
| 301 | } |
| 302 | if (packet_len > 2048) { |
| 303 | printk(PFX "RX: Too long packet detected\n"); |
| 304 | goto drop; |
| 305 | } |
| 306 | |
| 307 | /* FIXME Just usable for Promious Mode, for Manage mode exclude FCS */ |
| 308 | /* if (packet_len - sizeof(*agnxhdr) < FCS_LEN) { */ |
| 309 | /* printk(PFX "RX: Too short packet detected\n"); */ |
| 310 | /* goto drop; */ |
| 311 | /* } */ |
| 312 | return 0; |
| 313 | drop: |
| 314 | priv->stats.dot11FCSErrorCount++; |
| 315 | return -1; |
| 316 | } |
| 317 | |
| 318 | void handle_rx_irq(struct agnx_priv *priv) |
| 319 | { |
| 320 | struct ieee80211_rx_status status; |
| 321 | unsigned int len; |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 322 | /* AGNX_TRACE; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 323 | |
| 324 | do { |
| 325 | struct agnx_desc *desc; |
| 326 | u32 frag; |
| 327 | struct agnx_info *info; |
| 328 | struct agnx_hdr *hdr; |
| 329 | struct sk_buff *skb; |
| 330 | unsigned int i = priv->rx.idx % priv->rx.size; |
| 331 | |
| 332 | desc = priv->rx.desc + i; |
| 333 | frag = be32_to_cpu(desc->frag); |
| 334 | if (frag & OWNER) |
| 335 | break; |
| 336 | |
| 337 | info = priv->rx.info + i; |
| 338 | skb = info->skb; |
| 339 | hdr = (struct agnx_hdr *)(skb->data); |
| 340 | |
| 341 | len = (frag & PACKET_LEN) >> PACKET_LEN_SHIFT; |
| 342 | if (agnx_packet_check(priv, hdr, len) == -1) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 343 | rx_desc_reusing(priv, i); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 344 | continue; |
| 345 | } |
| 346 | skb_put(skb, len); |
| 347 | |
| 348 | do { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 349 | u16 fctl; |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 350 | fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr)->frame_control); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 351 | if ((fctl & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_BEACON)/* && !(fctl & IEEE80211_STYPE_BEACON)) */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 352 | dump_ieee80211_hdr((struct ieee80211_hdr *)hdr->mac_hdr, "RX"); |
| 353 | } while (0); |
| 354 | |
| 355 | if (hdr->_11b0 && !hdr->_11g0) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 356 | /* int j; |
| 357 | u16 fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr) |
| 358 | ->frame_control); |
| 359 | if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { |
| 360 | agnx_print_rx_hdr(hdr); |
| 361 | agnx_print_sta(priv, BSSID_STAID); |
| 362 | for (j = 0; j < 8; j++) |
| 363 | agnx_print_sta_tx_wq(priv, BSSID_STAID, j); |
| 364 | } */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 365 | |
| 366 | get_rx_stats(priv, hdr, &status); |
| 367 | skb_pull(skb, sizeof(*hdr)); |
| 368 | combine_hdr_frag((struct ieee80211_hdr *)hdr->mac_hdr, skb); |
| 369 | } else if (!hdr->_11b0 && hdr->_11g0) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 370 | /* int j; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 371 | agnx_print_rx_hdr(hdr); |
| 372 | agnx_print_sta(priv, BSSID_STAID); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 373 | /* for (j = 0; j < 8; j++) */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 374 | agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); |
| 375 | |
| 376 | print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, |
| 377 | skb->data, skb->len + 8); |
| 378 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 379 | /* if (agnx_plcp_get_bitrate_ofdm(&hdr->_11g0) == 0) */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 380 | get_rx_stats(priv, hdr, &status); |
| 381 | skb_pull(skb, sizeof(*hdr)); |
| 382 | combine_hdr_frag((struct ieee80211_hdr *) |
| 383 | ((void *)&hdr->mac_hdr), skb); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 384 | /* dump_ieee80211_hdr((struct ieee80211_hdr *)skb->data, "RX G"); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 385 | } else |
| 386 | agnx_bug("Unknown packets type"); |
Johannes Berg | f1d58c2 | 2009-06-17 13:13:00 +0200 | [diff] [blame] | 387 | memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); |
| 388 | ieee80211_rx_irqsafe(priv->hw, skb); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 389 | rx_desc_reinit(priv, i); |
| 390 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 391 | } while (priv->rx.idx++); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 392 | } /* handle_rx_irq */ |
| 393 | |
| 394 | static inline void handle_tx_irq(struct agnx_priv *priv, struct agnx_ring *ring) |
| 395 | { |
| 396 | struct agnx_desc *desc; |
| 397 | struct agnx_info *info; |
| 398 | unsigned int idx; |
| 399 | |
| 400 | for (idx = ring->idx_sent; idx < ring->idx; idx++) { |
| 401 | unsigned int i = idx % ring->size; |
| 402 | u32 frag; |
| 403 | |
| 404 | desc = ring->desc + i; |
| 405 | info = ring->info + i; |
| 406 | |
| 407 | frag = be32_to_cpu(desc->frag); |
| 408 | if (frag & OWNER) { |
| 409 | if (info->type == HEADER) |
| 410 | break; |
| 411 | else |
| 412 | agnx_bug("TX error"); |
| 413 | } |
| 414 | |
| 415 | pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_TODEVICE); |
| 416 | |
| 417 | do { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 418 | /* int j; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 419 | size_t len; |
| 420 | len = info->skb->len - sizeof(struct agnx_hdr) + info->hdr_len; |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 421 | /* if (len == 614) { */ |
| 422 | /* agnx_print_desc(desc); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 423 | if (info->type == PACKET) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 424 | /* agnx_print_tx_hdr((struct agnx_hdr *)info->skb->data); */ |
| 425 | /* agnx_print_sta_power(priv, LOCAL_STAID); */ |
| 426 | /* agnx_print_sta(priv, LOCAL_STAID); */ |
| 427 | /* for (j = 0; j < 8; j++) */ |
| 428 | /* agnx_print_sta_tx_wq(priv, LOCAL_STAID, 0); */ |
| 429 | /* agnx_print_sta_power(priv, BSSID_STAID); */ |
| 430 | /* agnx_print_sta(priv, BSSID_STAID); */ |
| 431 | /* for (j = 0; j < 8; j++) */ |
| 432 | /* agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 433 | } |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 434 | /* } */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 435 | } while (0); |
| 436 | |
| 437 | if (info->type == PACKET) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 438 | /* dump_txm_registers(priv); |
| 439 | dump_rxm_registers(priv); |
| 440 | dump_bm_registers(priv); |
| 441 | dump_cir_registers(priv); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | if (info->type == PACKET) { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 445 | /* struct ieee80211_hdr *hdr; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 446 | struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(info->skb); |
| 447 | |
| 448 | skb_pull(info->skb, sizeof(struct agnx_hdr)); |
| 449 | memcpy(skb_push(info->skb, info->hdr_len), &info->hdr, info->hdr_len); |
| 450 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 451 | /* dump_ieee80211_hdr((struct ieee80211_hdr *)info->skb->data, "TX_HANDLE"); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 452 | /* print_hex_dump_bytes("agnx: TX_HANDLE: ", DUMP_PREFIX_NONE, */ |
| 453 | /* info->skb->data, info->skb->len); */ |
| 454 | |
| 455 | if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) |
| 456 | txi->flags |= IEEE80211_TX_STAT_ACK; |
| 457 | |
| 458 | ieee80211_tx_status_irqsafe(priv->hw, info->skb); |
| 459 | |
| 460 | |
| 461 | /* info->tx_status.queue_number = (ring->size - i) / 2; */ |
| 462 | /* ieee80211_tx_status_irqsafe(priv->hw, info->skb, &(info->tx_status)); */ |
| 463 | /* } else */ |
| 464 | /* dev_kfree_skb_irq(info->skb); */ |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 465 | } |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 466 | memset(desc, 0, sizeof(*desc)); |
| 467 | memset(info, 0, sizeof(*info)); |
| 468 | } |
| 469 | |
| 470 | ring->idx_sent = idx; |
| 471 | /* TODO fill the priv->low_level_stats */ |
| 472 | |
| 473 | /* ieee80211_wake_queue(priv->hw, 0); */ |
| 474 | } |
| 475 | |
| 476 | void handle_txm_irq(struct agnx_priv *priv) |
| 477 | { |
| 478 | handle_tx_irq(priv, &priv->txm); |
| 479 | } |
| 480 | |
| 481 | void handle_txd_irq(struct agnx_priv *priv) |
| 482 | { |
| 483 | handle_tx_irq(priv, &priv->txd); |
| 484 | } |
| 485 | |
| 486 | void handle_other_irq(struct agnx_priv *priv) |
| 487 | { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 488 | /* void __iomem *ctl = priv->ctl; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 489 | u32 status = priv->irq_status; |
| 490 | void __iomem *ctl = priv->ctl; |
| 491 | u32 reg; |
| 492 | |
| 493 | if (status & IRQ_TX_BEACON) { |
| 494 | iowrite32(IRQ_TX_BEACON, ctl + AGNX_INT_STAT); |
| 495 | printk(PFX "IRQ: TX Beacon control is 0X%.8X\n", ioread32(ctl + AGNX_TXM_BEACON_CTL)); |
| 496 | printk(PFX "IRQ: TX Beacon rx frame num: %d\n", rx_frame_cnt); |
| 497 | } |
| 498 | if (status & IRQ_TX_RETRY) { |
| 499 | reg = ioread32(ctl + AGNX_TXM_RETRYSTAID); |
| 500 | printk(PFX "IRQ: TX Retry, RETRY STA ID is %x\n", reg); |
| 501 | } |
| 502 | if (status & IRQ_TX_ACTIVITY) |
| 503 | printk(PFX "IRQ: TX Activity\n"); |
| 504 | if (status & IRQ_RX_ACTIVITY) |
| 505 | printk(PFX "IRQ: RX Activity\n"); |
| 506 | if (status & IRQ_RX_X) |
| 507 | printk(PFX "IRQ: RX X\n"); |
| 508 | if (status & IRQ_RX_Y) { |
| 509 | reg = ioread32(ctl + AGNX_INT_MASK); |
| 510 | reg &= ~IRQ_RX_Y; |
| 511 | iowrite32(reg, ctl + AGNX_INT_MASK); |
| 512 | iowrite32(IRQ_RX_Y, ctl + AGNX_INT_STAT); |
| 513 | printk(PFX "IRQ: RX Y\n"); |
| 514 | } |
| 515 | if (status & IRQ_RX_HASHHIT) { |
| 516 | reg = ioread32(ctl + AGNX_INT_MASK); |
| 517 | reg &= ~IRQ_RX_HASHHIT; |
| 518 | iowrite32(reg, ctl + AGNX_INT_MASK); |
| 519 | iowrite32(IRQ_RX_HASHHIT, ctl + AGNX_INT_STAT); |
| 520 | printk(PFX "IRQ: RX Hash Hit\n"); |
| 521 | |
| 522 | } |
| 523 | if (status & IRQ_RX_FRAME) { |
| 524 | reg = ioread32(ctl + AGNX_INT_MASK); |
| 525 | reg &= ~IRQ_RX_FRAME; |
| 526 | iowrite32(reg, ctl + AGNX_INT_MASK); |
| 527 | iowrite32(IRQ_RX_FRAME, ctl + AGNX_INT_STAT); |
| 528 | printk(PFX "IRQ: RX Frame\n"); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 529 | rx_frame_cnt++; |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 530 | } |
| 531 | if (status & IRQ_ERR_INT) { |
| 532 | iowrite32(IRQ_ERR_INT, ctl + AGNX_INT_STAT); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 533 | /* agnx_hw_reset(priv); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 534 | printk(PFX "IRQ: Error Interrupt\n"); |
| 535 | } |
| 536 | if (status & IRQ_TX_QUE_FULL) |
| 537 | printk(PFX "IRQ: TX Workqueue Full\n"); |
| 538 | if (status & IRQ_BANDMAN_ERR) |
| 539 | printk(PFX "IRQ: Bandwidth Management Error\n"); |
| 540 | if (status & IRQ_TX_DISABLE) |
| 541 | printk(PFX "IRQ: TX Disable\n"); |
| 542 | if (status & IRQ_RX_IVASESKEY) |
| 543 | printk(PFX "IRQ: RX Invalid Session Key\n"); |
| 544 | if (status & IRQ_REP_THHIT) |
| 545 | printk(PFX "IRQ: Replay Threshold Hit\n"); |
| 546 | if (status & IRQ_TIMER1) |
| 547 | printk(PFX "IRQ: Timer1\n"); |
| 548 | if (status & IRQ_TIMER_CNT) |
| 549 | printk(PFX "IRQ: Timer Count\n"); |
| 550 | if (status & IRQ_PHY_FASTINT) |
| 551 | printk(PFX "IRQ: Phy Fast Interrupt\n"); |
| 552 | if (status & IRQ_PHY_SLOWINT) |
| 553 | printk(PFX "IRQ: Phy Slow Interrupt\n"); |
| 554 | if (status & IRQ_OTHER) |
| 555 | printk(PFX "IRQ: 0x80000000\n"); |
| 556 | } /* handle_other_irq */ |
| 557 | |
| 558 | |
| 559 | static inline void route_flag_set(struct agnx_hdr *txhdr) |
| 560 | { |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 561 | /* u32 reg = 0; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 562 | |
| 563 | /* FIXME */ |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 564 | /* reg = (0x7 << ROUTE_COMPRESSION_SHIFT) & ROUTE_COMPRESSION; */ |
| 565 | /* txhdr->reg5 = cpu_to_be32(reg); */ |
| 566 | txhdr->reg5 = (0xa << 0x0) | (0x7 << 0x18); |
| 567 | /* txhdr->reg5 = cpu_to_be32((0xa << 0x0) | (0x7 << 0x18)); */ |
| 568 | /* txhdr->reg5 = cpu_to_be32(0x7 << 0x0); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 569 | } |
| 570 | |
| 571 | /* Return 0 if no match */ |
| 572 | static inline unsigned int get_power_level(unsigned int rate, unsigned int antennas_num) |
| 573 | { |
| 574 | unsigned int power_level; |
| 575 | |
| 576 | switch (rate) { |
| 577 | case 10: |
| 578 | case 20: |
| 579 | case 55: |
| 580 | case 60: |
| 581 | case 90: |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 582 | case 120: |
| 583 | power_level = 22; |
| 584 | break; |
| 585 | |
| 586 | case 180: |
| 587 | power_level = 19; |
| 588 | break; |
| 589 | |
| 590 | case 240: |
| 591 | power_level = 18; |
| 592 | break; |
| 593 | |
| 594 | case 360: |
| 595 | power_level = 16; |
| 596 | break; |
| 597 | |
| 598 | case 480: |
| 599 | power_level = 15; |
| 600 | break; |
| 601 | |
| 602 | case 540: |
| 603 | power_level = 14; |
| 604 | break; |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 605 | default: |
| 606 | agnx_bug("Error rate setting\n"); |
| 607 | } |
| 608 | |
| 609 | if (power_level && (antennas_num == 2)) |
| 610 | power_level -= 3; |
| 611 | |
| 612 | return power_level; |
| 613 | } |
| 614 | |
| 615 | static inline void fill_agnx_hdr(struct agnx_priv *priv, struct agnx_info *tx_info) |
| 616 | { |
| 617 | struct agnx_hdr *txhdr = (struct agnx_hdr *)tx_info->skb->data; |
| 618 | size_t len; |
| 619 | u16 fc = le16_to_cpu(*(__le16 *)&tx_info->hdr); |
| 620 | u32 reg; |
| 621 | |
| 622 | memset(txhdr, 0, sizeof(*txhdr)); |
| 623 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 624 | /* reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, LOCAL_STAID); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 625 | reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, BSSID_STAID); |
| 626 | reg |= agnx_set_bits(WORKQUEUE_ID, WORKQUEUE_ID_SHIFT, 0); |
| 627 | txhdr->reg4 = cpu_to_be32(reg); |
| 628 | |
| 629 | /* Set the Hardware Sequence Number to 1? */ |
| 630 | reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 0); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 631 | /* reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 1); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 632 | reg |= agnx_set_bits(MAC_HDR_LEN, MAC_HDR_LEN_SHIFT, tx_info->hdr_len); |
| 633 | txhdr->reg1 = cpu_to_be32(reg); |
| 634 | /* Set the agnx_hdr's MAC header */ |
| 635 | memcpy(txhdr->mac_hdr, &tx_info->hdr, tx_info->hdr_len); |
| 636 | |
| 637 | reg = agnx_set_bits(ACK, ACK_SHIFT, 1); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 638 | /* reg = agnx_set_bits(ACK, ACK_SHIFT, 0); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 639 | reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 0); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 640 | /* reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 1); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 641 | reg |= agnx_set_bits(RELAY, RELAY_SHIFT, 0); |
| 642 | reg |= agnx_set_bits(TM, TM_SHIFT, 0); |
| 643 | txhdr->reg0 = cpu_to_be32(reg); |
| 644 | |
| 645 | /* Set the long and short retry limits */ |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 646 | txhdr->tx.short_retry_limit = tx_info->txi->control.rates[0].count; |
| 647 | txhdr->tx.long_retry_limit = tx_info->txi->control.rates[0].count; |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 648 | |
| 649 | /* FIXME */ |
| 650 | len = tx_info->skb->len - sizeof(*txhdr) + tx_info->hdr_len + FCS_LEN; |
| 651 | if (fc & IEEE80211_FCTL_PROTECTED) |
| 652 | len += 8; |
| 653 | len = 2398; |
| 654 | reg = agnx_set_bits(FRAG_SIZE, FRAG_SIZE_SHIFT, len); |
| 655 | len = tx_info->skb->len - sizeof(*txhdr); |
| 656 | reg |= agnx_set_bits(PAYLOAD_LEN, PAYLOAD_LEN_SHIFT, len); |
| 657 | txhdr->reg3 = cpu_to_be32(reg); |
| 658 | |
| 659 | route_flag_set(txhdr); |
| 660 | } /* fill_hdr */ |
| 661 | |
| 662 | static void txm_power_set(struct agnx_priv *priv, |
| 663 | struct ieee80211_tx_info *txi) |
| 664 | { |
| 665 | struct agnx_sta_power power; |
| 666 | u32 reg; |
| 667 | |
| 668 | /* FIXME */ |
Greg Kroah-Hartman | aa0d6c3 | 2009-01-05 12:37:19 -0800 | [diff] [blame] | 669 | if (txi->control.rates[0].idx < 0) { |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 670 | /* For B mode Short Preamble */ |
| 671 | reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_SHORT); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 672 | /* control->tx_rate = -control->tx_rate; */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 673 | } else |
| 674 | reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211G); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 675 | /* reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_LONG); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 676 | reg |= agnx_set_bits(SIGNAL, SIGNAL_SHIFT, 0xB); |
| 677 | reg |= agnx_set_bits(RATE, RATE_SHIFT, 0xB); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 678 | /* reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 15); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 679 | reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 20); |
| 680 | /* if rate < 11M set it to 0 */ |
| 681 | reg |= agnx_set_bits(NUM_TRANSMITTERS, NUM_TRANSMITTERS_SHIFT, 1); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 682 | /* reg |= agnx_set_bits(EDCF, EDCF_SHIFT, 1); */ |
| 683 | /* reg |= agnx_set_bits(TIFS, TIFS_SHIFT, 1); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 684 | |
| 685 | power.reg = reg; |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 686 | /* power.reg = cpu_to_le32(reg); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 687 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 688 | /* set_sta_power(priv, &power, LOCAL_STAID); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 689 | set_sta_power(priv, &power, BSSID_STAID); |
| 690 | } |
| 691 | |
| 692 | static inline int tx_packet_check(struct sk_buff *skb) |
| 693 | { |
| 694 | unsigned int ieee_len = ieee80211_get_hdrlen_from_skb(skb); |
| 695 | if (skb->len > 2048) { |
| 696 | printk(KERN_ERR PFX "length is %d\n", skb->len); |
| 697 | agnx_bug("Too long TX skb"); |
| 698 | return -1; |
| 699 | } |
| 700 | /* FIXME */ |
| 701 | if (skb->len == ieee_len) { |
| 702 | printk(PFX "A strange TX packet\n"); |
| 703 | return -1; |
| 704 | /* tx_faile_irqsafe(); */ |
| 705 | } |
| 706 | return 0; |
| 707 | } |
| 708 | |
| 709 | static int __agnx_tx(struct agnx_priv *priv, struct sk_buff *skb, |
| 710 | struct agnx_ring *ring) |
| 711 | { |
| 712 | struct agnx_desc *hdr_desc, *frag_desc; |
| 713 | struct agnx_info *hdr_info, *frag_info; |
| 714 | struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); |
| 715 | unsigned long flags; |
| 716 | unsigned int i; |
| 717 | |
| 718 | spin_lock_irqsave(&priv->lock, flags); |
| 719 | |
| 720 | /* The RX interrupt need be Disable until this TX packet |
| 721 | is handled in the next tx interrupt */ |
| 722 | disable_rx_interrupt(priv); |
| 723 | |
| 724 | i = ring->idx; |
| 725 | ring->idx += 2; |
| 726 | /* if (priv->txm_idx - priv->txm_idx_sent == AGNX_TXM_RING_SIZE - 2) */ |
| 727 | /* ieee80211_stop_queue(priv->hw, 0); */ |
| 728 | |
| 729 | /* Set agnx header's info and desc */ |
| 730 | i %= ring->size; |
| 731 | hdr_desc = ring->desc + i; |
| 732 | hdr_info = ring->info + i; |
| 733 | hdr_info->hdr_len = ieee80211_get_hdrlen_from_skb(skb); |
| 734 | memcpy(&hdr_info->hdr, skb->data, hdr_info->hdr_len); |
| 735 | |
| 736 | /* Add the agnx header to the front of the SKB */ |
| 737 | skb_push(skb, sizeof(struct agnx_hdr) - hdr_info->hdr_len); |
| 738 | |
| 739 | hdr_info->txi = txi; |
| 740 | hdr_info->dma_len = sizeof(struct agnx_hdr); |
| 741 | hdr_info->skb = skb; |
| 742 | hdr_info->type = HEADER; |
| 743 | fill_agnx_hdr(priv, hdr_info); |
| 744 | hdr_info->mapping = pci_map_single(priv->pdev, skb->data, |
| 745 | hdr_info->dma_len, PCI_DMA_TODEVICE); |
| 746 | do { |
| 747 | u32 frag = 0; |
| 748 | frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 1); |
| 749 | frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 0); |
| 750 | frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); |
| 751 | frag |= agnx_set_bits(FIRST_FRAG_LEN, FIRST_FRAG_LEN_SHIFT, 1); |
| 752 | frag |= agnx_set_bits(OWNER, OWNER_SHIFT, 1); |
| 753 | hdr_desc->frag = cpu_to_be32(frag); |
| 754 | } while (0); |
| 755 | hdr_desc->dma_addr = cpu_to_be32(hdr_info->mapping); |
| 756 | |
| 757 | |
| 758 | /* Set Frag's info and desc */ |
| 759 | i = (i + 1) % ring->size; |
| 760 | frag_desc = ring->desc + i; |
| 761 | frag_info = ring->info + i; |
| 762 | memcpy(frag_info, hdr_info, sizeof(struct agnx_info)); |
| 763 | frag_info->type = PACKET; |
| 764 | frag_info->dma_len = skb->len - hdr_info->dma_len; |
| 765 | frag_info->mapping = pci_map_single(priv->pdev, skb->data + hdr_info->dma_len, |
| 766 | frag_info->dma_len, PCI_DMA_TODEVICE); |
| 767 | do { |
| 768 | u32 frag = 0; |
| 769 | frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 0); |
| 770 | frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 1); |
| 771 | frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); |
| 772 | frag |= agnx_set_bits(SUB_FRAG_LEN, SUB_FRAG_LEN_SHIFT, frag_info->dma_len); |
| 773 | frag_desc->frag = cpu_to_be32(frag); |
| 774 | } while (0); |
| 775 | frag_desc->dma_addr = cpu_to_be32(frag_info->mapping); |
| 776 | |
| 777 | txm_power_set(priv, txi); |
| 778 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 779 | /* do { */ |
| 780 | /* int j; */ |
| 781 | /* size_t len; */ |
| 782 | /* len = skb->len - hdr_info->dma_len + hdr_info->hdr_len; */ |
| 783 | /* if (len == 614) { */ |
| 784 | /* agnx_print_desc(hdr_desc); */ |
| 785 | /* agnx_print_desc(frag_desc); */ |
| 786 | /* agnx_print_tx_hdr((struct agnx_hdr *)skb->data); */ |
| 787 | /* agnx_print_sta_power(priv, LOCAL_STAID); */ |
| 788 | /* agnx_print_sta(priv, LOCAL_STAID); */ |
| 789 | /* for (j = 0; j < 8; j++) */ |
| 790 | /* agnx_print_sta_tx_wq(priv, LOCAL_STAID, j); */ |
| 791 | /* agnx_print_sta_power(priv, BSSID_STAID); */ |
| 792 | /* agnx_print_sta(priv, BSSID_STAID); */ |
| 793 | /* for (j = 0; j < 8; j++) */ |
| 794 | /* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ |
| 795 | /* } */ |
| 796 | /* } while (0); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 797 | |
| 798 | spin_unlock_irqrestore(&priv->lock, flags); |
| 799 | |
| 800 | /* FIXME ugly code */ |
| 801 | /* Trigger TXM */ |
| 802 | do { |
| 803 | u32 reg; |
| 804 | reg = (ioread32(priv->ctl + AGNX_CIR_TXMCTL)); |
| 805 | reg |= 0x8; |
| 806 | iowrite32((reg), priv->ctl + AGNX_CIR_TXMCTL); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 807 | } while (0); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 808 | |
| 809 | /* Trigger TXD */ |
| 810 | do { |
| 811 | u32 reg; |
| 812 | reg = (ioread32(priv->ctl + AGNX_CIR_TXDCTL)); |
| 813 | reg |= 0x8; |
| 814 | iowrite32((reg), priv->ctl + AGNX_CIR_TXDCTL); |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 815 | } while (0); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 816 | |
| 817 | return 0; |
| 818 | } |
| 819 | |
| 820 | int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb) |
| 821 | { |
| 822 | u16 fctl; |
| 823 | |
| 824 | if (tx_packet_check(skb)) |
| 825 | return 0; |
| 826 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 827 | /* print_hex_dump_bytes("agnx: TX_PACKET: ", DUMP_PREFIX_NONE, */ |
| 828 | /* skb->data, skb->len); */ |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 829 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 830 | fctl = le16_to_cpu(*((__le16 *)skb->data)); |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 831 | |
Erik Andrén | fd1f72b | 2009-03-14 22:39:36 +0100 | [diff] [blame] | 832 | if ((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) |
Li YanBo | 0f22aab | 2008-10-27 20:32:57 -0700 | [diff] [blame] | 833 | return __agnx_tx(priv, skb, &priv->txd); |
| 834 | else |
| 835 | return __agnx_tx(priv, skb, &priv->txm); |
| 836 | } |