aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm/rx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-24 08:25:36 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-01 05:27:15 -0500
commit8ca151b568b67a7b72dcfc6ee6ea7c107ddd795c (patch)
treedac0236038f3791140e9f864c5db2be873c568f0 /drivers/net/wireless/iwlwifi/mvm/rx.c
parentb1e1adfa7d30cd0e8ad9a5c6a89e8c45ebe084f4 (diff)
iwlwifi: add the MVM driver
Newer firmware revisions have a completely new firmware API. This is the new driver for this new API. I've listed the people who directly contributed code, but many others from various teams have contributed in other ways. Cc: Alexander Bondar <alexander.bondar@intel.com> Cc: Amit Beka <amit.beka@intel.com> Cc: Amnon Paz <amnonx.paz@intel.com> Cc: Assaf Krauss <assaf.krauss@intel.com> Cc: David Spinadel <david.spinadel@intel.com> Cc: Dor Shaish <dor.shaish@intel.com> Cc: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Cc: Eytan Lifshitz <eytan.lifshitz@intel.com> Cc: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/rx.c')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c355
1 files changed, 355 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
new file mode 100644
index 000000000000..52da375e5740
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -0,0 +1,355 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called LICENSE.GPL.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *****************************************************************************/
62#include "iwl-trans.h"
63
64#include "mvm.h"
65#include "fw-api.h"
66
67/*
68 * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
69 *
70 * Copies the phy information in mvm->last_phy_info, it will be used when the
71 * actual data will come from the fw in the next packet.
72 */
73int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
74 struct iwl_device_cmd *cmd)
75{
76 struct iwl_rx_packet *pkt = rxb_addr(rxb);
77
78 memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
79 mvm->ampdu_ref++;
80 return 0;
81}
82
83/*
84 * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
85 *
86 * Adds the rxb to a new skb and give it to mac80211
87 */
88static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
89 struct ieee80211_hdr *hdr, u16 len,
90 u32 ampdu_status,
91 struct iwl_rx_cmd_buffer *rxb,
92 struct ieee80211_rx_status *stats)
93{
94 struct sk_buff *skb;
95 unsigned int hdrlen, fraglen;
96
97 /* Dont use dev_alloc_skb(), we'll have enough headroom once
98 * ieee80211_hdr pulled.
99 */
100 skb = alloc_skb(128, GFP_ATOMIC);
101 if (!skb) {
102 IWL_ERR(mvm, "alloc_skb failed\n");
103 return;
104 }
105 /* If frame is small enough to fit in skb->head, pull it completely.
106 * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
107 * are more efficient.
108 */
109 hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
110
111 memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
112 fraglen = len - hdrlen;
113
114 if (fraglen) {
115 int offset = (void *)hdr + hdrlen -
116 rxb_addr(rxb) + rxb_offset(rxb);
117
118 skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
119 fraglen, rxb->truesize);
120 }
121
122 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
123
124 ieee80211_rx(mvm->hw, skb);
125}
126
127/*
128 * iwl_mvm_calc_rssi - calculate the rssi in dBm
129 * @phy_info: the phy information for the coming packet
130 */
131static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
132 struct iwl_rx_phy_info *phy_info)
133{
134 u32 rssi_a, rssi_b, rssi_c, max_rssi, agc_db;
135 u32 val;
136
137 /* Find max rssi among 3 possible receivers.
138 * These values are measured by the Digital Signal Processor (DSP).
139 * They should stay fairly constant even as the signal strength varies,
140 * if the radio's Automatic Gain Control (AGC) is working right.
141 * AGC value (see below) will provide the "interesting" info.
142 */
143 val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
144 rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
145 rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
146 val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_C_IDX]);
147 rssi_c = (val & IWL_OFDM_RSSI_INBAND_C_MSK) >> IWL_OFDM_RSSI_C_POS;
148
149 val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
150 agc_db = (val & IWL_OFDM_AGC_DB_MSK) >> IWL_OFDM_AGC_DB_POS;
151
152 max_rssi = max_t(u32, rssi_a, rssi_b);
153 max_rssi = max_t(u32, max_rssi, rssi_c);
154
155 IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
156 rssi_a, rssi_b, rssi_c, max_rssi, agc_db);
157
158 /* dBm = max_rssi dB - agc dB - constant.
159 * Higher AGC (higher radio gain) means lower signal. */
160 return max_rssi - agc_db - IWL_RSSI_OFFSET;
161}
162
163/*
164 * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
165 * @mvm: the mvm object
166 * @hdr: 80211 header
167 * @stats: status in mac80211's format
168 * @rx_pkt_status: status coming from fw
169 *
170 * returns non 0 value if the packet should be dropped
171 */
172static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
173 struct ieee80211_hdr *hdr,
174 struct ieee80211_rx_status *stats,
175 u32 rx_pkt_status)
176{
177 if (!ieee80211_has_protected(hdr->frame_control) ||
178 (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
179 RX_MPDU_RES_STATUS_SEC_NO_ENC)
180 return 0;
181
182 /* packet was encrypted with unknown alg */
183 if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
184 RX_MPDU_RES_STATUS_SEC_ENC_ERR)
185 return 0;
186
187 switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
188 case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
189 /* alg is CCM: check MIC only */
190 if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
191 return -1;
192
193 stats->flag |= RX_FLAG_DECRYPTED;
194 IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
195 return 0;
196
197 case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
198 /* Don't drop the frame and decrypt it in SW */
199 if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
200 return 0;
201 /* fall through if TTAK OK */
202
203 case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
204 if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
205 return -1;
206
207 stats->flag |= RX_FLAG_DECRYPTED;
208 return 0;
209
210 default:
211 IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
212 }
213
214 return 0;
215}
216
217/*
218 * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
219 *
220 * Handles the actual data of the Rx packet from the fw
221 */
222int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
223 struct iwl_device_cmd *cmd)
224{
225 struct ieee80211_hdr *hdr;
226 struct ieee80211_rx_status rx_status = {};
227 struct iwl_rx_packet *pkt = rxb_addr(rxb);
228 struct iwl_rx_phy_info *phy_info;
229 struct iwl_rx_mpdu_res_start *rx_res;
230 u32 len;
231 u32 ampdu_status;
232 u32 rate_n_flags;
233 u32 rx_pkt_status;
234
235 phy_info = &mvm->last_phy_info;
236 rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
237 hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
238 len = le16_to_cpu(rx_res->byte_count);
239 rx_pkt_status = le32_to_cpup((__le32 *)
240 (pkt->data + sizeof(*rx_res) + len));
241
242 memset(&rx_status, 0, sizeof(rx_status));
243
244 /*
245 * drop the packet if it has failed being decrypted by HW
246 */
247 if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
248 IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
249 rx_pkt_status);
250 return 0;
251 }
252
253 if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
254 IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
255 phy_info->cfg_phy_cnt);
256 return 0;
257 }
258
259 if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
260 !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
261 IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
262 return 0;
263 }
264
265 /* This will be used in several places later */
266 rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
267
268 /* rx_status carries information about the packet to mac80211 */
269 rx_status.mactime = le64_to_cpu(phy_info->timestamp);
270 rx_status.band =
271 (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
272 IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
273 rx_status.freq =
274 ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
275 rx_status.band);
276 /*
277 * TSF as indicated by the fw is at INA time, but mac80211 expects the
278 * TSF at the beginning of the MPDU.
279 */
280 /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
281
282 /* Find max signal strength (dBm) among 3 antenna/receiver chains */
283 rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
284
285 IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
286 (unsigned long long)rx_status.mactime);
287
288 /*
289 * "antenna number"
290 *
291 * It seems that the antenna field in the phy flags value
292 * is actually a bit field. This is undefined by radiotap,
293 * it wants an actual antenna number but I always get "7"
294 * for most legacy frames I receive indicating that the
295 * same frame was received on all three RX chains.
296 *
297 * I think this field should be removed in favor of a
298 * new 802.11n radiotap field "RX chains" that is defined
299 * as a bitmask.
300 */
301 rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
302 RX_RES_PHY_FLAGS_ANTENNA)
303 >> RX_RES_PHY_FLAGS_ANTENNA_POS;
304
305 /* set the preamble flag if appropriate */
306 if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
307 rx_status.flag |= RX_FLAG_SHORTPRE;
308
309 if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
310 /*
311 * We know which subframes of an A-MPDU belong
312 * together since we get a single PHY response
313 * from the firmware for all of them
314 */
315 rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
316 rx_status.ampdu_reference = mvm->ampdu_ref;
317 }
318
319 /* Set up the HT phy flags */
320 switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
321 case RATE_MCS_CHAN_WIDTH_20:
322 break;
323 case RATE_MCS_CHAN_WIDTH_40:
324 rx_status.flag |= RX_FLAG_40MHZ;
325 break;
326 case RATE_MCS_CHAN_WIDTH_80:
327 rx_status.flag |= RX_FLAG_80MHZ;
328 break;
329 case RATE_MCS_CHAN_WIDTH_160:
330 rx_status.flag |= RX_FLAG_160MHZ;
331 break;
332 }
333 if (rate_n_flags & RATE_MCS_SGI_MSK)
334 rx_status.flag |= RX_FLAG_SHORT_GI;
335 if (rate_n_flags & RATE_HT_MCS_GF_MSK)
336 rx_status.flag |= RX_FLAG_HT_GF;
337 if (rate_n_flags & RATE_MCS_HT_MSK) {
338 rx_status.flag |= RX_FLAG_HT;
339 rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
340 } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
341 rx_status.vht_nss =
342 ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
343 RATE_VHT_MCS_NSS_POS) + 1;
344 rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
345 rx_status.flag |= RX_FLAG_VHT;
346 } else {
347 rx_status.rate_idx =
348 iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
349 rx_status.band);
350 }
351
352 iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
353 rxb, &rx_status);
354 return 0;
355}