aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorWey-Yi Guy <wey-yi.w.guy@intel.com>2010-01-22 17:22:43 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-01-25 16:36:19 -0500
commitafbdd69af0e6a0c40676d4d4b94a0a4414708eaa (patch)
tree08218339f6b9a3eb3ff690a9aa94777913fe4cba /drivers/net/wireless
parenta13d276f1e49ae0bc4ad18ce8ea3c90656c9e8d4 (diff)
iwlwifi: add function to reset/tune radio if needed
Adding "radio reset" function to help reset and stabilize the radio. During normal operation, sometime for unknown reason, radio encounter problem and can not recover by itself; the best way to recover from it is to reset and re-tune the radio. Currently, there is no RF reset command available, but since radio will get reset when switching channel, use internal hw scan request to force radio reset and get back to normal operation state. The internal hw scan will only perform passive scan on the first available channel (not the channel being used) in associated state. The request should be ignored if already performing scan operation or STA is not in associated state. Also include an "internal_scan" debugfs file to help trigger the internal scan from user mode. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c155
6 files changed, 193 insertions, 14 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index bb3ed25f8438..645bc133577a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -3343,6 +3343,30 @@ int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display)
3343} 3343}
3344EXPORT_SYMBOL(iwl_dump_fh); 3344EXPORT_SYMBOL(iwl_dump_fh);
3345 3345
3346void iwl_force_rf_reset(struct iwl_priv *priv)
3347{
3348 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
3349 return;
3350
3351 if (!iwl_is_associated(priv)) {
3352 IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
3353 return;
3354 }
3355 /*
3356 * There is no easy and better way to force reset the radio,
3357 * the only known method is switching channel which will force to
3358 * reset and tune the radio.
3359 * Use internal short scan (single channel) operation to should
3360 * achieve this objective.
3361 * Driver should reset the radio when number of consecutive missed
3362 * beacon, or any other uCode error condition detected.
3363 */
3364 IWL_DEBUG_INFO(priv, "perform radio reset.\n");
3365 iwl_internal_short_hw_scan(priv);
3366 return;
3367}
3368EXPORT_SYMBOL(iwl_force_rf_reset);
3369
3346#ifdef CONFIG_PM 3370#ifdef CONFIG_PM
3347 3371
3348int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state) 3372int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 785331a98aa5..6de83d1e1eb8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -494,6 +494,8 @@ void iwl_init_scan_params(struct iwl_priv *priv);
494int iwl_scan_cancel(struct iwl_priv *priv); 494int iwl_scan_cancel(struct iwl_priv *priv);
495int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); 495int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
496int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); 496int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
497int iwl_internal_short_hw_scan(struct iwl_priv *priv);
498void iwl_force_rf_reset(struct iwl_priv *priv);
497u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, 499u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
498 const u8 *ie, int ie_len, int left); 500 const u8 *ie, int ie_len, int left);
499void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); 501void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 36b558f23325..d81b4f39bb1d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -113,6 +113,7 @@ struct iwl_debugfs {
113 struct dentry *file_ucode_tracing; 113 struct dentry *file_ucode_tracing;
114 struct dentry *file_fh_reg; 114 struct dentry *file_fh_reg;
115 struct dentry *file_missed_beacon; 115 struct dentry *file_missed_beacon;
116 struct dentry *file_internal_scan;
116 } dbgfs_debug_files; 117 } dbgfs_debug_files;
117 u32 sram_offset; 118 u32 sram_offset;
118 u32 sram_len; 119 u32 sram_len;
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 02f80bc21307..4944fdb31ba8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -2174,6 +2174,27 @@ static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file,
2174 return count; 2174 return count;
2175} 2175}
2176 2176
2177static ssize_t iwl_dbgfs_internal_scan_write(struct file *file,
2178 const char __user *user_buf,
2179 size_t count, loff_t *ppos)
2180{
2181 struct iwl_priv *priv = file->private_data;
2182 char buf[8];
2183 int buf_size;
2184 int scan;
2185
2186 memset(buf, 0, sizeof(buf));
2187 buf_size = min(count, sizeof(buf) - 1);
2188 if (copy_from_user(buf, user_buf, buf_size))
2189 return -EFAULT;
2190 if (sscanf(buf, "%d", &scan) != 1)
2191 return -EINVAL;
2192
2193 iwl_internal_short_hw_scan(priv);
2194
2195 return count;
2196}
2197
2177DEBUGFS_READ_FILE_OPS(rx_statistics); 2198DEBUGFS_READ_FILE_OPS(rx_statistics);
2178DEBUGFS_READ_FILE_OPS(tx_statistics); 2199DEBUGFS_READ_FILE_OPS(tx_statistics);
2179DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); 2200DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@@ -2192,6 +2213,7 @@ DEBUGFS_WRITE_FILE_OPS(csr);
2192DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); 2213DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
2193DEBUGFS_READ_FILE_OPS(fh_reg); 2214DEBUGFS_READ_FILE_OPS(fh_reg);
2194DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); 2215DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon);
2216DEBUGFS_WRITE_FILE_OPS(internal_scan);
2195 2217
2196/* 2218/*
2197 * Create the debugfs files and directories 2219 * Create the debugfs files and directories
@@ -2245,6 +2267,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
2245 DEBUGFS_ADD_FILE(csr, debug, S_IWUSR); 2267 DEBUGFS_ADD_FILE(csr, debug, S_IWUSR);
2246 DEBUGFS_ADD_FILE(fh_reg, debug, S_IRUSR); 2268 DEBUGFS_ADD_FILE(fh_reg, debug, S_IRUSR);
2247 DEBUGFS_ADD_FILE(missed_beacon, debug, S_IWUSR); 2269 DEBUGFS_ADD_FILE(missed_beacon, debug, S_IWUSR);
2270 DEBUGFS_ADD_FILE(internal_scan, debug, S_IWUSR);
2248 if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { 2271 if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
2249 DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR); 2272 DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR);
2250 DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR); 2273 DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR);
@@ -2306,6 +2329,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
2306 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_csr); 2329 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_csr);
2307 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_fh_reg); 2330 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_fh_reg);
2308 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_missed_beacon); 2331 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_missed_beacon);
2332 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_internal_scan);
2309 if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { 2333 if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
2310 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. 2334 DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
2311 file_ucode_rx_stats); 2335 file_ucode_rx_stats);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 5e06e666f176..502d7a6b0904 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1080,6 +1080,7 @@ struct iwl_priv {
1080 void *scan; 1080 void *scan;
1081 int scan_bands; 1081 int scan_bands;
1082 struct cfg80211_scan_request *scan_request; 1082 struct cfg80211_scan_request *scan_request;
1083 bool is_internal_short_scan;
1083 u8 scan_tx_ant[IEEE80211_NUM_BANDS]; 1084 u8 scan_tx_ant[IEEE80211_NUM_BANDS];
1084 u8 mgmt_tx_ant; 1085 u8 mgmt_tx_ant;
1085 1086
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index ceb91f969e45..fd6bafbddfca 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -314,6 +314,72 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
314} 314}
315EXPORT_SYMBOL(iwl_get_passive_dwell_time); 315EXPORT_SYMBOL(iwl_get_passive_dwell_time);
316 316
317static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
318 enum ieee80211_band band,
319 struct iwl_scan_channel *scan_ch)
320{
321 const struct ieee80211_supported_band *sband;
322 const struct iwl_channel_info *ch_info;
323 u16 passive_dwell = 0;
324 u16 active_dwell = 0;
325 int i, added = 0;
326 u16 channel = 0;
327
328 sband = iwl_get_hw_mode(priv, band);
329 if (!sband) {
330 IWL_ERR(priv, "invalid band\n");
331 return added;
332 }
333
334 active_dwell = iwl_get_active_dwell_time(priv, band, 0);
335 passive_dwell = iwl_get_passive_dwell_time(priv, band);
336
337 if (passive_dwell <= active_dwell)
338 passive_dwell = active_dwell + 1;
339
340 /* only scan single channel, good enough to reset the RF */
341 /* pick the first valid not in-use channel */
342 if (band == IEEE80211_BAND_5GHZ) {
343 for (i = 14; i < priv->channel_count; i++) {
344 if (priv->channel_info[i].channel !=
345 le16_to_cpu(priv->staging_rxon.channel)) {
346 channel = priv->channel_info[i].channel;
347 ch_info = iwl_get_channel_info(priv,
348 band, channel);
349 if (is_channel_valid(ch_info))
350 break;
351 }
352 }
353 } else {
354 for (i = 0; i < 14; i++) {
355 if (priv->channel_info[i].channel !=
356 le16_to_cpu(priv->staging_rxon.channel)) {
357 channel =
358 priv->channel_info[i].channel;
359 ch_info = iwl_get_channel_info(priv,
360 band, channel);
361 if (is_channel_valid(ch_info))
362 break;
363 }
364 }
365 }
366 if (channel) {
367 scan_ch->channel = cpu_to_le16(channel);
368 scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
369 scan_ch->active_dwell = cpu_to_le16(active_dwell);
370 scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
371 /* Set txpower levels to defaults */
372 scan_ch->dsp_atten = 110;
373 if (band == IEEE80211_BAND_5GHZ)
374 scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
375 else
376 scan_ch->tx_gain = ((1 << 5) | (5 << 3));
377 added++;
378 } else
379 IWL_ERR(priv, "no valid channel found\n");
380 return added;
381}
382
317static int iwl_get_channels_for_scan(struct iwl_priv *priv, 383static int iwl_get_channels_for_scan(struct iwl_priv *priv,
318 enum ieee80211_band band, 384 enum ieee80211_band band,
319 u8 is_active, u8 n_probes, 385 u8 is_active, u8 n_probes,
@@ -421,6 +487,7 @@ static int iwl_scan_initiate(struct iwl_priv *priv)
421 487
422 IWL_DEBUG_INFO(priv, "Starting scan...\n"); 488 IWL_DEBUG_INFO(priv, "Starting scan...\n");
423 set_bit(STATUS_SCANNING, &priv->status); 489 set_bit(STATUS_SCANNING, &priv->status);
490 priv->is_internal_short_scan = false;
424 priv->scan_start = jiffies; 491 priv->scan_start = jiffies;
425 priv->scan_pass_start = priv->scan_start; 492 priv->scan_pass_start = priv->scan_start;
426 493
@@ -488,6 +555,45 @@ out_unlock:
488} 555}
489EXPORT_SYMBOL(iwl_mac_hw_scan); 556EXPORT_SYMBOL(iwl_mac_hw_scan);
490 557
558/*
559 * internal short scan, this function should only been called while associated.
560 * It will reset and tune the radio to prevent possible RF related problem
561 */
562int iwl_internal_short_hw_scan(struct iwl_priv *priv)
563{
564 int ret = 0;
565
566 if (!iwl_is_ready_rf(priv)) {
567 ret = -EIO;
568 IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
569 goto out;
570 }
571 if (test_bit(STATUS_SCANNING, &priv->status)) {
572 IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
573 ret = -EAGAIN;
574 goto out;
575 }
576 if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
577 IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
578 ret = -EAGAIN;
579 goto out;
580 }
581 priv->scan_bands = 0;
582 if (priv->band == IEEE80211_BAND_5GHZ)
583 priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
584 else
585 priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
586
587 IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
588 set_bit(STATUS_SCANNING, &priv->status);
589 priv->is_internal_short_scan = true;
590 queue_work(priv->workqueue, &priv->request_scan);
591
592out:
593 return ret;
594}
595EXPORT_SYMBOL(iwl_internal_short_hw_scan);
596
491#define IWL_SCAN_CHECK_WATCHDOG (7 * HZ) 597#define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
492 598
493void iwl_bg_scan_check(struct work_struct *data) 599void iwl_bg_scan_check(struct work_struct *data)
@@ -551,7 +657,8 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
551 if (WARN_ON(left < ie_len)) 657 if (WARN_ON(left < ie_len))
552 return len; 658 return len;
553 659
554 memcpy(pos, ies, ie_len); 660 if (ies)
661 memcpy(pos, ies, ie_len);
555 len += ie_len; 662 len += ie_len;
556 left -= ie_len; 663 left -= ie_len;
557 664
@@ -654,7 +761,6 @@ static void iwl_bg_request_scan(struct work_struct *data)
654 unsigned long flags; 761 unsigned long flags;
655 762
656 IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); 763 IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
657
658 spin_lock_irqsave(&priv->lock, flags); 764 spin_lock_irqsave(&priv->lock, flags);
659 interval = priv->beacon_int; 765 interval = priv->beacon_int;
660 spin_unlock_irqrestore(&priv->lock, flags); 766 spin_unlock_irqrestore(&priv->lock, flags);
@@ -672,7 +778,9 @@ static void iwl_bg_request_scan(struct work_struct *data)
672 scan_suspend_time, interval); 778 scan_suspend_time, interval);
673 } 779 }
674 780
675 if (priv->scan_request->n_ssids) { 781 if (priv->is_internal_short_scan) {
782 IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
783 } else if (priv->scan_request->n_ssids) {
676 int i, p = 0; 784 int i, p = 0;
677 IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); 785 IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
678 for (i = 0; i < priv->scan_request->n_ssids; i++) { 786 for (i = 0; i < priv->scan_request->n_ssids; i++) {
@@ -753,24 +861,38 @@ static void iwl_bg_request_scan(struct work_struct *data)
753 rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; 861 rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
754 rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; 862 rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
755 scan->rx_chain = cpu_to_le16(rx_chain); 863 scan->rx_chain = cpu_to_le16(rx_chain);
756 cmd_len = iwl_fill_probe_req(priv, 864 if (!priv->is_internal_short_scan) {
757 (struct ieee80211_mgmt *)scan->data, 865 cmd_len = iwl_fill_probe_req(priv,
758 priv->scan_request->ie, 866 (struct ieee80211_mgmt *)scan->data,
759 priv->scan_request->ie_len, 867 priv->scan_request->ie,
760 IWL_MAX_SCAN_SIZE - sizeof(*scan)); 868 priv->scan_request->ie_len,
869 IWL_MAX_SCAN_SIZE - sizeof(*scan));
870 } else {
871 cmd_len = iwl_fill_probe_req(priv,
872 (struct ieee80211_mgmt *)scan->data,
873 NULL, 0,
874 IWL_MAX_SCAN_SIZE - sizeof(*scan));
761 875
876 }
762 scan->tx_cmd.len = cpu_to_le16(cmd_len); 877 scan->tx_cmd.len = cpu_to_le16(cmd_len);
763
764 if (iwl_is_monitor_mode(priv)) 878 if (iwl_is_monitor_mode(priv))
765 scan->filter_flags = RXON_FILTER_PROMISC_MSK; 879 scan->filter_flags = RXON_FILTER_PROMISC_MSK;
766 880
767 scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | 881 scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
768 RXON_FILTER_BCON_AWARE_MSK); 882 RXON_FILTER_BCON_AWARE_MSK);
769 883
770 scan->channel_count = 884 if (priv->is_internal_short_scan) {
771 iwl_get_channels_for_scan(priv, band, is_active, n_probes, 885 scan->channel_count =
772 (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); 886 iwl_get_single_channel_for_scan(priv, band,
773 887 (void *)&scan->data[le16_to_cpu(
888 scan->tx_cmd.len)]);
889 } else {
890 scan->channel_count =
891 iwl_get_channels_for_scan(priv, band,
892 is_active, n_probes,
893 (void *)&scan->data[le16_to_cpu(
894 scan->tx_cmd.len)]);
895 }
774 if (scan->channel_count == 0) { 896 if (scan->channel_count == 0) {
775 IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); 897 IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
776 goto done; 898 goto done;
@@ -831,7 +953,12 @@ void iwl_bg_scan_completed(struct work_struct *work)
831 953
832 cancel_delayed_work(&priv->scan_check); 954 cancel_delayed_work(&priv->scan_check);
833 955
834 ieee80211_scan_completed(priv->hw, false); 956 if (!priv->is_internal_short_scan)
957 ieee80211_scan_completed(priv->hw, false);
958 else {
959 priv->is_internal_short_scan = false;
960 IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
961 }
835 962
836 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 963 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
837 return; 964 return;