Commit | Line | Data |
---|---|---|
a55360e4 TW |
1 | /****************************************************************************** |
2 | * | |
1f447808 | 3 | * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. |
a55360e4 TW |
4 | * |
5 | * Portions of this file are derived from the ipw3945 project, as well | |
6 | * as portions of the ieee80211 subsystem header files. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of version 2 of the GNU General Public License as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
20 | * | |
21 | * The full GNU General Public License is included in this distribution in the | |
22 | * file called LICENSE. | |
23 | * | |
24 | * Contact Information: | |
759ef89f | 25 | * Intel Linux Wireless <ilw@linux.intel.com> |
a55360e4 TW |
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
27 | * | |
28 | *****************************************************************************/ | |
29 | ||
1781a07f | 30 | #include <linux/etherdevice.h> |
5a0e3ad6 | 31 | #include <linux/slab.h> |
a55360e4 | 32 | #include <net/mac80211.h> |
a05ffd39 | 33 | #include <asm/unaligned.h> |
a55360e4 TW |
34 | #include "iwl-eeprom.h" |
35 | #include "iwl-dev.h" | |
36 | #include "iwl-core.h" | |
37 | #include "iwl-sta.h" | |
38 | #include "iwl-io.h" | |
c1354754 | 39 | #include "iwl-calib.h" |
a55360e4 TW |
40 | #include "iwl-helpers.h" |
41 | /************************** RX-FUNCTIONS ****************************/ | |
42 | /* | |
43 | * Rx theory of operation | |
44 | * | |
45 | * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), | |
46 | * each of which point to Receive Buffers to be filled by the NIC. These get | |
47 | * used not only for Rx frames, but for any command response or notification | |
48 | * from the NIC. The driver and NIC manage the Rx buffers by means | |
49 | * of indexes into the circular buffer. | |
50 | * | |
51 | * Rx Queue Indexes | |
52 | * The host/firmware share two index registers for managing the Rx buffers. | |
53 | * | |
54 | * The READ index maps to the first position that the firmware may be writing | |
55 | * to -- the driver can read up to (but not including) this position and get | |
56 | * good data. | |
57 | * The READ index is managed by the firmware once the card is enabled. | |
58 | * | |
59 | * The WRITE index maps to the last position the driver has read from -- the | |
60 | * position preceding WRITE is the last slot the firmware can place a packet. | |
61 | * | |
62 | * The queue is empty (no good data) if WRITE = READ - 1, and is full if | |
63 | * WRITE = READ. | |
64 | * | |
65 | * During initialization, the host sets up the READ queue position to the first | |
66 | * INDEX position, and WRITE to the last (READ - 1 wrapped) | |
67 | * | |
68 | * When the firmware places a packet in a buffer, it will advance the READ index | |
69 | * and fire the RX interrupt. The driver can then query the READ index and | |
70 | * process as many packets as possible, moving the WRITE index forward as it | |
71 | * resets the Rx queue buffers with new memory. | |
72 | * | |
73 | * The management in the driver is as follows: | |
74 | * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When | |
75 | * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled | |
76 | * to replenish the iwl->rxq->rx_free. | |
77 | * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the | |
78 | * iwl->rxq is replenished and the READ INDEX is updated (updating the | |
79 | * 'processed' and 'read' driver indexes as well) | |
80 | * + A received packet is processed and handed to the kernel network stack, | |
81 | * detached from the iwl->rxq. The driver 'processed' index is updated. | |
82 | * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free | |
83 | * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ | |
84 | * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there | |
85 | * were enough free buffers and RX_STALLED is set it is cleared. | |
86 | * | |
87 | * | |
88 | * Driver sequence: | |
89 | * | |
90 | * iwl_rx_queue_alloc() Allocates rx_free | |
91 | * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls | |
92 | * iwl_rx_queue_restock | |
93 | * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx | |
94 | * queue, updates firmware pointers, and updates | |
95 | * the WRITE index. If insufficient rx_free buffers | |
96 | * are available, schedules iwl_rx_replenish | |
97 | * | |
98 | * -- enable interrupts -- | |
99 | * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the | |
100 | * READ INDEX, detaching the SKB from the pool. | |
101 | * Moves the packet buffer from queue to rx_used. | |
102 | * Calls iwl_rx_queue_restock to refill any empty | |
103 | * slots. | |
104 | * ... | |
105 | * | |
106 | */ | |
107 | ||
108 | /** | |
109 | * iwl_rx_queue_space - Return number of free slots available in queue. | |
110 | */ | |
111 | int iwl_rx_queue_space(const struct iwl_rx_queue *q) | |
112 | { | |
113 | int s = q->read - q->write; | |
114 | if (s <= 0) | |
115 | s += RX_QUEUE_SIZE; | |
116 | /* keep some buffer to not confuse full and empty queue */ | |
117 | s -= 2; | |
118 | if (s < 0) | |
119 | s = 0; | |
120 | return s; | |
121 | } | |
122 | EXPORT_SYMBOL(iwl_rx_queue_space); | |
123 | ||
124 | /** | |
125 | * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue | |
126 | */ | |
7bfedc59 | 127 | void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q) |
a55360e4 | 128 | { |
a55360e4 | 129 | unsigned long flags; |
141c43a3 WT |
130 | u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg; |
131 | u32 reg; | |
a55360e4 TW |
132 | |
133 | spin_lock_irqsave(&q->lock, flags); | |
134 | ||
135 | if (q->need_update == 0) | |
136 | goto exit_unlock; | |
137 | ||
138 | /* If power-saving is in use, make sure device is awake */ | |
139 | if (test_bit(STATUS_POWER_PMI, &priv->status)) { | |
140 | reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); | |
141 | ||
142 | if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { | |
309e731a BC |
143 | IWL_DEBUG_INFO(priv, "Rx queue requesting wakeup, GP1 = 0x%x\n", |
144 | reg); | |
a55360e4 TW |
145 | iwl_set_bit(priv, CSR_GP_CNTRL, |
146 | CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | |
147 | goto exit_unlock; | |
148 | } | |
149 | ||
4752c93c MA |
150 | q->write_actual = (q->write & ~0x7); |
151 | iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual); | |
a55360e4 TW |
152 | |
153 | /* Else device is assumed to be awake */ | |
141c43a3 | 154 | } else { |
a55360e4 | 155 | /* Device expects a multiple of 8 */ |
4752c93c MA |
156 | q->write_actual = (q->write & ~0x7); |
157 | iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual); | |
141c43a3 | 158 | } |
a55360e4 TW |
159 | |
160 | q->need_update = 0; | |
161 | ||
162 | exit_unlock: | |
163 | spin_unlock_irqrestore(&q->lock, flags); | |
a55360e4 TW |
164 | } |
165 | EXPORT_SYMBOL(iwl_rx_queue_update_write_ptr); | |
a55360e4 TW |
166 | |
167 | int iwl_rx_queue_alloc(struct iwl_priv *priv) | |
168 | { | |
169 | struct iwl_rx_queue *rxq = &priv->rxq; | |
f36d04ab | 170 | struct device *dev = &priv->pci_dev->dev; |
a55360e4 TW |
171 | int i; |
172 | ||
173 | spin_lock_init(&rxq->lock); | |
174 | INIT_LIST_HEAD(&rxq->rx_free); | |
175 | INIT_LIST_HEAD(&rxq->rx_used); | |
176 | ||
177 | /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ | |
f36d04ab SG |
178 | rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr, |
179 | GFP_KERNEL); | |
a55360e4 | 180 | if (!rxq->bd) |
8d86422a WT |
181 | goto err_bd; |
182 | ||
f36d04ab SG |
183 | rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status), |
184 | &rxq->rb_stts_dma, GFP_KERNEL); | |
8d86422a WT |
185 | if (!rxq->rb_stts) |
186 | goto err_rb; | |
a55360e4 TW |
187 | |
188 | /* Fill the rx_used queue with _all_ of the Rx buffers */ | |
189 | for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) | |
190 | list_add_tail(&rxq->pool[i].list, &rxq->rx_used); | |
191 | ||
192 | /* Set us so that we have processed and used all buffers, but have | |
193 | * not restocked the Rx queue with fresh buffers */ | |
194 | rxq->read = rxq->write = 0; | |
4752c93c | 195 | rxq->write_actual = 0; |
a55360e4 TW |
196 | rxq->free_count = 0; |
197 | rxq->need_update = 0; | |
198 | return 0; | |
8d86422a WT |
199 | |
200 | err_rb: | |
f36d04ab SG |
201 | dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, |
202 | rxq->dma_addr); | |
8d86422a WT |
203 | err_bd: |
204 | return -ENOMEM; | |
a55360e4 TW |
205 | } |
206 | EXPORT_SYMBOL(iwl_rx_queue_alloc); | |
207 | ||
c1354754 TW |
208 | void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, |
209 | struct iwl_rx_mem_buffer *rxb) | |
210 | ||
211 | { | |
2f301227 | 212 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
2aa6ab86 | 213 | struct iwl_missed_beacon_notif *missed_beacon; |
c1354754 TW |
214 | |
215 | missed_beacon = &pkt->u.missed_beacon; | |
a13d276f WYG |
216 | if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > |
217 | priv->missed_beacon_threshold) { | |
e1623446 | 218 | IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n", |
a13d276f | 219 | le32_to_cpu(missed_beacon->consecutive_missed_beacons), |
c1354754 TW |
220 | le32_to_cpu(missed_beacon->total_missed_becons), |
221 | le32_to_cpu(missed_beacon->num_recvd_beacons), | |
222 | le32_to_cpu(missed_beacon->num_expected_beacons)); | |
223 | if (!test_bit(STATUS_SCANNING, &priv->status)) | |
224 | iwl_init_sensitivity(priv); | |
225 | } | |
c1354754 TW |
226 | } |
227 | EXPORT_SYMBOL(iwl_rx_missed_beacon_notif); | |
8f91aecb | 228 | |
81963d68 RC |
229 | void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, |
230 | struct iwl_rx_mem_buffer *rxb) | |
231 | { | |
232 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
233 | struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); | |
234 | ||
235 | if (!report->state) { | |
236 | IWL_DEBUG_11H(priv, | |
237 | "Spectrum Measure Notification: Start\n"); | |
238 | return; | |
239 | } | |
240 | ||
241 | memcpy(&priv->measure_report, report, sizeof(*report)); | |
242 | priv->measurement_status |= MEASUREMENT_READY; | |
243 | } | |
244 | EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif); | |
245 | ||
246 | ||
8f91aecb EG |
247 | |
248 | /* Calculate noise level, based on measurements during network silence just | |
249 | * before arriving beacon. This measurement can be done only if we know | |
250 | * exactly when to expect beacons, therefore only when we're associated. */ | |
251 | static void iwl_rx_calc_noise(struct iwl_priv *priv) | |
252 | { | |
253 | struct statistics_rx_non_phy *rx_info | |
2daf6c15 | 254 | = &(priv->statistics.rx.general); |
8f91aecb EG |
255 | int num_active_rx = 0; |
256 | int total_silence = 0; | |
257 | int bcn_silence_a = | |
258 | le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; | |
259 | int bcn_silence_b = | |
260 | le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; | |
261 | int bcn_silence_c = | |
262 | le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; | |
ed1b6e99 | 263 | int last_rx_noise; |
8f91aecb EG |
264 | |
265 | if (bcn_silence_a) { | |
266 | total_silence += bcn_silence_a; | |
267 | num_active_rx++; | |
268 | } | |
269 | if (bcn_silence_b) { | |
270 | total_silence += bcn_silence_b; | |
271 | num_active_rx++; | |
272 | } | |
273 | if (bcn_silence_c) { | |
274 | total_silence += bcn_silence_c; | |
275 | num_active_rx++; | |
276 | } | |
277 | ||
278 | /* Average among active antennas */ | |
279 | if (num_active_rx) | |
ed1b6e99 | 280 | last_rx_noise = (total_silence / num_active_rx) - 107; |
8f91aecb | 281 | else |
ed1b6e99 | 282 | last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; |
8f91aecb | 283 | |
e1623446 | 284 | IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", |
8f91aecb | 285 | bcn_silence_a, bcn_silence_b, bcn_silence_c, |
ed1b6e99 | 286 | last_rx_noise); |
8f91aecb EG |
287 | } |
288 | ||
d73e4923 | 289 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
92a35bda WYG |
290 | /* |
291 | * based on the assumption of all statistics counter are in DWORD | |
292 | * FIXME: This function is for debugging, do not deal with | |
293 | * the case of counters roll-over. | |
294 | */ | |
295 | static void iwl_accumulative_statistics(struct iwl_priv *priv, | |
296 | __le32 *stats) | |
297 | { | |
298 | int i; | |
299 | __le32 *prev_stats; | |
300 | u32 *accum_stats; | |
e3ef2164 | 301 | u32 *delta, *max_delta; |
92a35bda | 302 | |
2daf6c15 JL |
303 | prev_stats = (__le32 *)&priv->statistics; |
304 | accum_stats = (u32 *)&priv->accum_statistics; | |
305 | delta = (u32 *)&priv->delta_statistics; | |
306 | max_delta = (u32 *)&priv->max_delta; | |
92a35bda WYG |
307 | |
308 | for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics); | |
e3ef2164 WYG |
309 | i += sizeof(__le32), stats++, prev_stats++, delta++, |
310 | max_delta++, accum_stats++) { | |
311 | if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { | |
312 | *delta = (le32_to_cpu(*stats) - | |
92a35bda | 313 | le32_to_cpu(*prev_stats)); |
e3ef2164 WYG |
314 | *accum_stats += *delta; |
315 | if (*delta > *max_delta) | |
316 | *max_delta = *delta; | |
317 | } | |
318 | } | |
92a35bda WYG |
319 | |
320 | /* reset accumulative statistics for "no-counter" type statistics */ | |
2daf6c15 JL |
321 | priv->accum_statistics.general.temperature = |
322 | priv->statistics.general.temperature; | |
323 | priv->accum_statistics.general.temperature_m = | |
324 | priv->statistics.general.temperature_m; | |
325 | priv->accum_statistics.general.ttl_timestamp = | |
326 | priv->statistics.general.ttl_timestamp; | |
327 | priv->accum_statistics.tx.tx_power.ant_a = | |
328 | priv->statistics.tx.tx_power.ant_a; | |
329 | priv->accum_statistics.tx.tx_power.ant_b = | |
330 | priv->statistics.tx.tx_power.ant_b; | |
331 | priv->accum_statistics.tx.tx_power.ant_c = | |
332 | priv->statistics.tx.tx_power.ant_c; | |
92a35bda WYG |
333 | } |
334 | #endif | |
335 | ||
8f91aecb EG |
336 | #define REG_RECALIB_PERIOD (60) |
337 | ||
fa8f130c WYG |
338 | /** |
339 | * iwl_good_plcp_health - checks for plcp error. | |
340 | * | |
341 | * When the plcp error is exceeding the thresholds, reset the radio | |
342 | * to improve the throughput. | |
343 | */ | |
344 | bool iwl_good_plcp_health(struct iwl_priv *priv, | |
345 | struct iwl_rx_packet *pkt) | |
346 | { | |
347 | bool rc = true; | |
348 | int combined_plcp_delta; | |
349 | unsigned int plcp_msec; | |
350 | unsigned long plcp_received_jiffies; | |
8f91aecb | 351 | |
3e4fb5fa TAN |
352 | /* |
353 | * check for plcp_err and trigger radio reset if it exceeds | |
354 | * the plcp error threshold plcp_delta. | |
355 | */ | |
356 | plcp_received_jiffies = jiffies; | |
357 | plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies - | |
358 | (long) priv->plcp_jiffies); | |
359 | priv->plcp_jiffies = plcp_received_jiffies; | |
360 | /* | |
361 | * check to make sure plcp_msec is not 0 to prevent division | |
362 | * by zero. | |
363 | */ | |
364 | if (plcp_msec) { | |
365 | combined_plcp_delta = | |
366 | (le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err) - | |
2daf6c15 | 367 | le32_to_cpu(priv->statistics.rx.ofdm.plcp_err)) + |
3e4fb5fa | 368 | (le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err) - |
2daf6c15 | 369 | le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); |
3e4fb5fa TAN |
370 | |
371 | if ((combined_plcp_delta > 0) && | |
beac5498 | 372 | ((combined_plcp_delta * 100) / plcp_msec) > |
3e4fb5fa TAN |
373 | priv->cfg->plcp_delta_threshold) { |
374 | /* | |
beac5498 WYG |
375 | * if plcp_err exceed the threshold, |
376 | * the following data is printed in csv format: | |
3e4fb5fa TAN |
377 | * Text: plcp_err exceeded %d, |
378 | * Received ofdm.plcp_err, | |
379 | * Current ofdm.plcp_err, | |
380 | * Received ofdm_ht.plcp_err, | |
381 | * Current ofdm_ht.plcp_err, | |
382 | * combined_plcp_delta, | |
383 | * plcp_msec | |
384 | */ | |
fa8f130c WYG |
385 | IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " |
386 | "%u, %u, %u, %u, %d, %u mSecs\n", | |
3e4fb5fa TAN |
387 | priv->cfg->plcp_delta_threshold, |
388 | le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), | |
2daf6c15 | 389 | le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), |
3e4fb5fa TAN |
390 | le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), |
391 | le32_to_cpu( | |
2daf6c15 | 392 | priv->statistics.rx.ofdm_ht.plcp_err), |
3e4fb5fa | 393 | combined_plcp_delta, plcp_msec); |
fa8f130c WYG |
394 | rc = false; |
395 | } | |
396 | } | |
397 | return rc; | |
398 | } | |
399 | EXPORT_SYMBOL(iwl_good_plcp_health); | |
400 | ||
a29576a7 | 401 | void iwl_recover_from_statistics(struct iwl_priv *priv, |
fa8f130c WYG |
402 | struct iwl_rx_packet *pkt) |
403 | { | |
404 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | |
405 | return; | |
406 | if (iwl_is_associated(priv)) { | |
407 | if (priv->cfg->ops->lib->check_ack_health) { | |
408 | if (!priv->cfg->ops->lib->check_ack_health( | |
409 | priv, pkt)) { | |
410 | /* | |
411 | * low ack count detected | |
412 | * restart Firmware | |
413 | */ | |
414 | IWL_ERR(priv, "low ack count detected, " | |
415 | "restart firmware\n"); | |
3d38f173 WYG |
416 | if (!iwl_force_reset(priv, IWL_FW_RESET)) |
417 | return; | |
fa8f130c | 418 | } |
3d38f173 WYG |
419 | } |
420 | if (priv->cfg->ops->lib->check_plcp_health) { | |
fa8f130c WYG |
421 | if (!priv->cfg->ops->lib->check_plcp_health( |
422 | priv, pkt)) { | |
423 | /* | |
424 | * high plcp error detected | |
425 | * reset Radio | |
426 | */ | |
427 | iwl_force_reset(priv, IWL_RF_RESET); | |
428 | } | |
3e4fb5fa TAN |
429 | } |
430 | } | |
beac5498 | 431 | } |
a29576a7 | 432 | EXPORT_SYMBOL(iwl_recover_from_statistics); |
beac5498 WYG |
433 | |
434 | void iwl_rx_statistics(struct iwl_priv *priv, | |
435 | struct iwl_rx_mem_buffer *rxb) | |
436 | { | |
437 | int change; | |
438 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
439 | ||
440 | ||
441 | IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", | |
2daf6c15 | 442 | (int)sizeof(priv->statistics), |
beac5498 WYG |
443 | le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); |
444 | ||
2daf6c15 | 445 | change = ((priv->statistics.general.temperature != |
beac5498 | 446 | pkt->u.stats.general.temperature) || |
2daf6c15 | 447 | ((priv->statistics.flag & |
beac5498 WYG |
448 | STATISTICS_REPLY_FLG_HT40_MODE_MSK) != |
449 | (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); | |
450 | ||
d73e4923 | 451 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
beac5498 WYG |
452 | iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); |
453 | #endif | |
fa8f130c | 454 | iwl_recover_from_statistics(priv, pkt); |
3e4fb5fa | 455 | |
2daf6c15 | 456 | memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); |
8f91aecb EG |
457 | |
458 | set_bit(STATUS_STATISTICS, &priv->status); | |
459 | ||
460 | /* Reschedule the statistics timer to occur in | |
461 | * REG_RECALIB_PERIOD seconds to ensure we get a | |
462 | * thermal update even if the uCode doesn't give | |
463 | * us one */ | |
464 | mod_timer(&priv->statistics_periodic, jiffies + | |
465 | msecs_to_jiffies(REG_RECALIB_PERIOD * 1000)); | |
466 | ||
467 | if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && | |
468 | (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { | |
469 | iwl_rx_calc_noise(priv); | |
470 | queue_work(priv->workqueue, &priv->run_time_calib_work); | |
471 | } | |
62161aef WYG |
472 | if (priv->cfg->ops->lib->temp_ops.temperature && change) |
473 | priv->cfg->ops->lib->temp_ops.temperature(priv); | |
8f91aecb EG |
474 | } |
475 | EXPORT_SYMBOL(iwl_rx_statistics); | |
1781a07f | 476 | |
ef8d5529 WYG |
477 | void iwl_reply_statistics(struct iwl_priv *priv, |
478 | struct iwl_rx_mem_buffer *rxb) | |
479 | { | |
480 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
481 | ||
482 | if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { | |
d73e4923 | 483 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
2daf6c15 | 484 | memset(&priv->accum_statistics, 0, |
ef8d5529 | 485 | sizeof(struct iwl_notif_statistics)); |
2daf6c15 | 486 | memset(&priv->delta_statistics, 0, |
e3ef2164 | 487 | sizeof(struct iwl_notif_statistics)); |
2daf6c15 | 488 | memset(&priv->max_delta, 0, |
e3ef2164 | 489 | sizeof(struct iwl_notif_statistics)); |
ef8d5529 WYG |
490 | #endif |
491 | IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); | |
492 | } | |
493 | iwl_rx_statistics(priv, rxb); | |
494 | } | |
495 | EXPORT_SYMBOL(iwl_reply_statistics); | |
496 | ||
1781a07f EG |
497 | /* |
498 | * returns non-zero if packet should be dropped | |
499 | */ | |
8ccde88a SO |
500 | int iwl_set_decrypted_flag(struct iwl_priv *priv, |
501 | struct ieee80211_hdr *hdr, | |
502 | u32 decrypt_res, | |
503 | struct ieee80211_rx_status *stats) | |
1781a07f EG |
504 | { |
505 | u16 fc = le16_to_cpu(hdr->frame_control); | |
506 | ||
507 | if (priv->active_rxon.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) | |
508 | return 0; | |
509 | ||
510 | if (!(fc & IEEE80211_FCTL_PROTECTED)) | |
511 | return 0; | |
512 | ||
e1623446 | 513 | IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); |
1781a07f EG |
514 | switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { |
515 | case RX_RES_STATUS_SEC_TYPE_TKIP: | |
516 | /* The uCode has got a bad phase 1 Key, pushes the packet. | |
517 | * Decryption will be done in SW. */ | |
518 | if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == | |
519 | RX_RES_STATUS_BAD_KEY_TTAK) | |
520 | break; | |
521 | ||
522 | case RX_RES_STATUS_SEC_TYPE_WEP: | |
523 | if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == | |
524 | RX_RES_STATUS_BAD_ICV_MIC) { | |
525 | /* bad ICV, the packet is destroyed since the | |
526 | * decryption is inplace, drop it */ | |
e1623446 | 527 | IWL_DEBUG_RX(priv, "Packet destroyed\n"); |
1781a07f EG |
528 | return -1; |
529 | } | |
530 | case RX_RES_STATUS_SEC_TYPE_CCMP: | |
531 | if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == | |
532 | RX_RES_STATUS_DECRYPT_OK) { | |
e1623446 | 533 | IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); |
1781a07f EG |
534 | stats->flag |= RX_FLAG_DECRYPTED; |
535 | } | |
536 | break; | |
537 | ||
538 | default: | |
539 | break; | |
540 | } | |
541 | return 0; | |
542 | } | |
8ccde88a | 543 | EXPORT_SYMBOL(iwl_set_decrypted_flag); |