| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 1 | /* | 
|  | 2 | * This file is part of wl1271 | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2009 Nokia Corporation | 
|  | 5 | * | 
|  | 6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | 
|  | 7 | * | 
|  | 8 | * This program is free software; you can redistribute it and/or | 
|  | 9 | * modify it under the terms of the GNU General Public License | 
|  | 10 | * version 2 as published by the Free Software Foundation. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, but | 
|  | 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 15 | * General Public License for more details. | 
|  | 16 | * | 
|  | 17 | * You should have received a copy of the GNU General Public License | 
|  | 18 | * along with this program; if not, write to the Free Software | 
|  | 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 
|  | 20 | * 02110-1301 USA | 
|  | 21 | * | 
|  | 22 | */ | 
|  | 23 |  | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 24 | #include <linux/gfp.h> | 
|  | 25 |  | 
| Shahar Levi | 00d2010 | 2010-11-08 11:20:10 +0000 | [diff] [blame] | 26 | #include "wl12xx.h" | 
|  | 27 | #include "acx.h" | 
|  | 28 | #include "reg.h" | 
|  | 29 | #include "rx.h" | 
|  | 30 | #include "io.h" | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 31 |  | 
| Eliad Peller | c8bde24 | 2011-02-02 09:59:35 +0200 | [diff] [blame] | 32 | static u8 wl1271_rx_get_mem_block(struct wl1271_fw_common_status *status, | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 33 | u32 drv_rx_counter) | 
|  | 34 | { | 
| Luciano Coelho | d0f63b2 | 2009-10-15 10:33:29 +0300 | [diff] [blame] | 35 | return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & | 
|  | 36 | RX_MEM_BLOCK_MASK; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 37 | } | 
|  | 38 |  | 
| Eliad Peller | c8bde24 | 2011-02-02 09:59:35 +0200 | [diff] [blame] | 39 | static u32 wl1271_rx_get_buf_size(struct wl1271_fw_common_status *status, | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 40 | u32 drv_rx_counter) | 
|  | 41 | { | 
| Luciano Coelho | d0f63b2 | 2009-10-15 10:33:29 +0300 | [diff] [blame] | 42 | return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & | 
|  | 43 | RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 44 | } | 
|  | 45 |  | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 46 | static void wl1271_rx_status(struct wl1271 *wl, | 
|  | 47 | struct wl1271_rx_descriptor *desc, | 
|  | 48 | struct ieee80211_rx_status *status, | 
|  | 49 | u8 beacon) | 
|  | 50 | { | 
| Teemu Paasikivi | 6a2de93 | 2010-10-14 11:00:04 +0200 | [diff] [blame] | 51 | enum ieee80211_band desc_band; | 
|  | 52 |  | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 53 | memset(status, 0, sizeof(struct ieee80211_rx_status)); | 
|  | 54 |  | 
| Juuso Oikarinen | f876bb9 | 2010-03-26 12:53:11 +0200 | [diff] [blame] | 55 | status->band = wl->band; | 
| Teemu Paasikivi | 6a2de93 | 2010-10-14 11:00:04 +0200 | [diff] [blame] | 56 |  | 
|  | 57 | if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) | 
|  | 58 | desc_band = IEEE80211_BAND_2GHZ; | 
|  | 59 | else | 
|  | 60 | desc_band = IEEE80211_BAND_5GHZ; | 
|  | 61 |  | 
|  | 62 | status->rate_idx = wl1271_rate_to_idx(desc->rate, desc_band); | 
| Teemu Paasikivi | a410264 | 2009-10-13 12:47:51 +0300 | [diff] [blame] | 63 |  | 
| Shahar Levi | 00d2010 | 2010-11-08 11:20:10 +0000 | [diff] [blame] | 64 | #ifdef CONFIG_WL12XX_HT | 
| Shahar Levi | 1835785 | 2010-10-13 16:09:41 +0200 | [diff] [blame] | 65 | /* 11n support */ | 
|  | 66 | if (desc->rate <= CONF_HW_RXTX_RATE_MCS0) | 
|  | 67 | status->flag |= RX_FLAG_HT; | 
|  | 68 | #endif | 
|  | 69 |  | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 70 | status->signal = desc->rssi; | 
|  | 71 |  | 
| John W. Linville | ece550d | 2010-07-28 16:41:06 -0400 | [diff] [blame] | 72 | /* | 
|  | 73 | * FIXME: In wl1251, the SNR should be divided by two.  In wl1271 we | 
|  | 74 | * need to divide by two for now, but TI has been discussing about | 
|  | 75 | * changing it.  This needs to be rechecked. | 
|  | 76 | */ | 
|  | 77 | wl->noise = desc->rssi - (desc->snr >> 1); | 
|  | 78 |  | 
| Bruno Randolf | 59eb21a | 2011-01-17 13:37:28 +0900 | [diff] [blame] | 79 | status->freq = ieee80211_channel_to_frequency(desc->channel, desc_band); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 80 |  | 
|  | 81 | if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { | 
|  | 82 | status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; | 
|  | 83 |  | 
| Teemu Paasikivi | 5d07b66 | 2009-10-13 12:47:52 +0300 | [diff] [blame] | 84 | if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL))) | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 85 | status->flag |= RX_FLAG_DECRYPTED; | 
| Teemu Paasikivi | 5d07b66 | 2009-10-13 12:47:52 +0300 | [diff] [blame] | 86 | if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL)) | 
|  | 87 | status->flag |= RX_FLAG_MMIC_ERROR; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 88 | } | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 89 | } | 
|  | 90 |  | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 91 | static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 92 | { | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 93 | struct wl1271_rx_descriptor *desc; | 
|  | 94 | struct sk_buff *skb; | 
| Eliad Peller | 92fe9b5 | 2011-02-09 12:25:14 +0200 | [diff] [blame] | 95 | struct ieee80211_hdr *hdr; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 96 | u8 *buf; | 
|  | 97 | u8 beacon = 0; | 
|  | 98 |  | 
| Kalle Valo | 93c5bb6 | 2010-02-22 08:38:30 +0200 | [diff] [blame] | 99 | /* | 
|  | 100 | * In PLT mode we seem to get frames and mac80211 warns about them, | 
|  | 101 | * workaround this by not retrieving them at all. | 
|  | 102 | */ | 
|  | 103 | if (unlikely(wl->state == WL1271_STATE_PLT)) | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 104 | return -EINVAL; | 
| Kalle Valo | 93c5bb6 | 2010-02-22 08:38:30 +0200 | [diff] [blame] | 105 |  | 
| Luis R. Rodriguez | e9a6269 | 2009-11-02 12:15:15 -0500 | [diff] [blame] | 106 | skb = __dev_alloc_skb(length, GFP_KERNEL); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 107 | if (!skb) { | 
|  | 108 | wl1271_error("Couldn't allocate RX frame"); | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 109 | return -ENOMEM; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 110 | } | 
|  | 111 |  | 
|  | 112 | buf = skb_put(skb, length); | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 113 | memcpy(buf, data, length); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 114 |  | 
|  | 115 | /* the data read starts with the descriptor */ | 
|  | 116 | desc = (struct wl1271_rx_descriptor *) buf; | 
|  | 117 |  | 
|  | 118 | /* now we pull the descriptor out of the buffer */ | 
|  | 119 | skb_pull(skb, sizeof(*desc)); | 
|  | 120 |  | 
| Eliad Peller | 92fe9b5 | 2011-02-09 12:25:14 +0200 | [diff] [blame] | 121 | hdr = (struct ieee80211_hdr *)skb->data; | 
|  | 122 | if (ieee80211_is_beacon(hdr->frame_control)) | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 123 | beacon = 1; | 
|  | 124 |  | 
| Eliad Peller | 58be460 | 2010-09-19 18:55:08 +0200 | [diff] [blame] | 125 | wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 126 |  | 
|  | 127 | wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, | 
|  | 128 | beacon ? "beacon" : ""); | 
|  | 129 |  | 
| Juuso Oikarinen | b9f2e39 | 2010-05-14 10:46:24 +0300 | [diff] [blame] | 130 | skb_trim(skb, skb->len - desc->pad_len); | 
|  | 131 |  | 
| Ido Yariv | a620865 | 2011-03-01 15:14:41 +0200 | [diff] [blame] | 132 | skb_queue_tail(&wl->deferred_rx_queue, skb); | 
|  | 133 | ieee80211_queue_work(wl->hw, &wl->netstack_work); | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 134 |  | 
|  | 135 | return 0; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 136 | } | 
|  | 137 |  | 
| Eliad Peller | c8bde24 | 2011-02-02 09:59:35 +0200 | [diff] [blame] | 138 | void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 139 | { | 
|  | 140 | struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; | 
|  | 141 | u32 buf_size; | 
|  | 142 | u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; | 
|  | 143 | u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 144 | u32 rx_counter; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 145 | u32 mem_block; | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 146 | u32 pkt_length; | 
|  | 147 | u32 pkt_offset; | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 148 |  | 
|  | 149 | while (drv_rx_counter != fw_rx_counter) { | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 150 | buf_size = 0; | 
|  | 151 | rx_counter = drv_rx_counter; | 
|  | 152 | while (rx_counter != fw_rx_counter) { | 
|  | 153 | pkt_length = wl1271_rx_get_buf_size(status, rx_counter); | 
|  | 154 | if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE) | 
|  | 155 | break; | 
|  | 156 | buf_size += pkt_length; | 
|  | 157 | rx_counter++; | 
|  | 158 | rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; | 
|  | 159 | } | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 160 |  | 
|  | 161 | if (buf_size == 0) { | 
|  | 162 | wl1271_warning("received empty data"); | 
|  | 163 | break; | 
|  | 164 | } | 
|  | 165 |  | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 166 | /* | 
|  | 167 | * Choose the block we want to read | 
|  | 168 | * For aggregated packets, only the first memory block should | 
|  | 169 | * be retrieved. The FW takes care of the rest. | 
|  | 170 | */ | 
|  | 171 | mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); | 
| Luciano Coelho | d0f63b2 | 2009-10-15 10:33:29 +0300 | [diff] [blame] | 172 | wl->rx_mem_pool_addr.addr = (mem_block << 8) + | 
|  | 173 | le32_to_cpu(wl_mem_map->packet_memory_pool_start); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 174 | wl->rx_mem_pool_addr.addr_extra = | 
|  | 175 | wl->rx_mem_pool_addr.addr + 4; | 
| Teemu Paasikivi | 7b048c5 | 2010-02-18 13:25:55 +0200 | [diff] [blame] | 176 | wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 177 | sizeof(wl->rx_mem_pool_addr), false); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 178 |  | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 179 | /* Read all available packets at once */ | 
|  | 180 | wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, | 
|  | 181 | buf_size, true); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 182 |  | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 183 | /* Split data into separate packets */ | 
|  | 184 | pkt_offset = 0; | 
|  | 185 | while (pkt_offset < buf_size) { | 
|  | 186 | pkt_length = wl1271_rx_get_buf_size(status, | 
|  | 187 | drv_rx_counter); | 
| Juuso Oikarinen | fb2382c | 2010-10-25 11:24:29 +0200 | [diff] [blame] | 188 | /* | 
|  | 189 | * the handle data call can only fail in memory-outage | 
|  | 190 | * conditions, in that case the received frame will just | 
|  | 191 | * be dropped. | 
|  | 192 | */ | 
|  | 193 | wl1271_rx_handle_data(wl, | 
|  | 194 | wl->aggr_buf + pkt_offset, | 
|  | 195 | pkt_length); | 
| Ido Yariv | 1f37cbc | 2010-09-30 13:28:27 +0200 | [diff] [blame] | 196 | wl->rx_counter++; | 
|  | 197 | drv_rx_counter++; | 
|  | 198 | drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; | 
|  | 199 | pkt_offset += pkt_length; | 
|  | 200 | } | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 201 | } | 
| Ido Yariv | 606ea9f | 2011-03-01 15:14:39 +0200 | [diff] [blame] | 202 |  | 
|  | 203 | /* | 
|  | 204 | * Write the driver's packet counter to the FW. This is only required | 
|  | 205 | * for older hardware revisions | 
|  | 206 | */ | 
|  | 207 | if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) | 
|  | 208 | wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); | 
| Luciano Coelho | f5fc0f8 | 2009-08-06 16:25:28 +0300 | [diff] [blame] | 209 | } | 
| Arik Nemtsov | ae113b5 | 2010-10-16 18:45:07 +0200 | [diff] [blame] | 210 |  | 
|  | 211 | void wl1271_set_default_filters(struct wl1271 *wl) | 
|  | 212 | { | 
|  | 213 | if (wl->bss_type == BSS_TYPE_AP_BSS) { | 
|  | 214 | wl->rx_config = WL1271_DEFAULT_AP_RX_CONFIG; | 
|  | 215 | wl->rx_filter = WL1271_DEFAULT_AP_RX_FILTER; | 
|  | 216 | } else { | 
|  | 217 | wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG; | 
|  | 218 | wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER; | 
|  | 219 | } | 
|  | 220 | } |