aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>2014-10-01 08:05:24 -0400
committerJohn W. Linville <linville@tuxdriver.com>2014-10-02 14:23:14 -0400
commitc33407a8c50430f1634a8809f9528b6888360e56 (patch)
treec4be2eee3ece31482e46388bd4873c62e8d69d4d
parente6664dff0608440f117b0348594b887cb9725e4f (diff)
wil6210: manual FW error recovery mode
Introduce manual FW recovery mode. It is activated if module parameter @no_fw_recovery set to true. May be changed at runtime. Recovery information provided by new "recovery" debugfs file. It prints: mode = [auto|manual] state = [idle|pending|running] In manual mode, after FW error, recovery won't start automatically. Instead, after notification to user space, recovery waits in "pending" state, as indicated by the "recovery" debugfs file. User space tools may perform data collection and allow to continue recovery by writing "run" to the "recovery" debugfs file. Alternatively, recovery pending may be canceled by stopping network interface i.e. 'ifconfig wlan0 down' Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c67
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c46
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h12
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c1
5 files changed, 121 insertions, 9 deletions
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index f3a31e8c2535..d9f4b30dd343 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -728,6 +728,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
728 wil_print_bcon_data(bcon); 728 wil_print_bcon_data(bcon);
729 } 729 }
730 730
731 wil_set_recovery_state(wil, fw_recovery_idle);
732
731 mutex_lock(&wil->mutex); 733 mutex_lock(&wil->mutex);
732 734
733 __wil_down(wil); 735 __wil_down(wil);
@@ -775,6 +777,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
775 777
776 wil_dbg_misc(wil, "%s()\n", __func__); 778 wil_dbg_misc(wil, "%s()\n", __func__);
777 779
780 wil_set_recovery_state(wil, fw_recovery_idle);
781
778 mutex_lock(&wil->mutex); 782 mutex_lock(&wil->mutex);
779 783
780 rc = wmi_pcp_stop(wil); 784 rc = wmi_pcp_stop(wil);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index eb2204e5fdd4..54a6ddc6301b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1041,6 +1041,71 @@ static const struct file_operations fops_info = {
1041 .llseek = seq_lseek, 1041 .llseek = seq_lseek,
1042}; 1042};
1043 1043
1044/*---------recovery------------*/
1045/* mode = [manual|auto]
1046 * state = [idle|pending|running]
1047 */
1048static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
1049 size_t count, loff_t *ppos)
1050{
1051 struct wil6210_priv *wil = file->private_data;
1052 char buf[80];
1053 int n;
1054 static const char * const sstate[] = {"idle", "pending", "running"};
1055
1056 n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
1057 no_fw_recovery ? "manual" : "auto",
1058 sstate[wil->recovery_state]);
1059
1060 n = min_t(int, n, sizeof(buf));
1061
1062 return simple_read_from_buffer(user_buf, count, ppos,
1063 buf, n);
1064}
1065
1066static ssize_t wil_write_file_recovery(struct file *file,
1067 const char __user *buf_,
1068 size_t count, loff_t *ppos)
1069{
1070 struct wil6210_priv *wil = file->private_data;
1071 static const char run_command[] = "run";
1072 char buf[sizeof(run_command) + 1]; /* to detect "runx" */
1073 ssize_t rc;
1074
1075 if (wil->recovery_state != fw_recovery_pending) {
1076 wil_err(wil, "No recovery pending\n");
1077 return -EINVAL;
1078 }
1079
1080 if (*ppos != 0) {
1081 wil_err(wil, "Offset [%d]\n", (int)*ppos);
1082 return -EINVAL;
1083 }
1084
1085 if (count > sizeof(buf)) {
1086 wil_err(wil, "Input too long, len = %d\n", (int)count);
1087 return -EINVAL;
1088 }
1089
1090 rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
1091 if (rc < 0)
1092 return rc;
1093
1094 buf[rc] = '\0';
1095 if (0 == strcmp(buf, run_command))
1096 wil_set_recovery_state(wil, fw_recovery_running);
1097 else
1098 wil_err(wil, "Bad recovery command \"%s\"\n", buf);
1099
1100 return rc;
1101}
1102
1103static const struct file_operations fops_recovery = {
1104 .read = wil_read_file_recovery,
1105 .write = wil_write_file_recovery,
1106 .open = simple_open,
1107};
1108
1044/*---------Station matrix------------*/ 1109/*---------Station matrix------------*/
1045static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) 1110static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
1046{ 1111{
@@ -1152,6 +1217,7 @@ static const struct {
1152 {"freq", S_IRUGO, &fops_freq}, 1217 {"freq", S_IRUGO, &fops_freq},
1153 {"link", S_IRUGO, &fops_link}, 1218 {"link", S_IRUGO, &fops_link},
1154 {"info", S_IRUGO, &fops_info}, 1219 {"info", S_IRUGO, &fops_info},
1220 {"recovery", S_IRUGO | S_IWUSR, &fops_recovery},
1155}; 1221};
1156 1222
1157static void wil6210_debugfs_init_files(struct wil6210_priv *wil, 1223static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
@@ -1194,6 +1260,7 @@ static const struct dbg_off dbg_wil_off[] = {
1194 WIL_FIELD(status, S_IRUGO | S_IWUSR, doff_ulong), 1260 WIL_FIELD(status, S_IRUGO | S_IWUSR, doff_ulong),
1195 WIL_FIELD(fw_version, S_IRUGO, doff_u32), 1261 WIL_FIELD(fw_version, S_IRUGO, doff_u32),
1196 WIL_FIELD(hw_version, S_IRUGO, doff_x32), 1262 WIL_FIELD(hw_version, S_IRUGO, doff_x32),
1263 WIL_FIELD(recovery_count, S_IRUGO, doff_u32),
1197 {}, 1264 {},
1198}; 1265};
1199 1266
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 0424763fd5a1..6500caf8d609 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -25,9 +25,9 @@
25#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000 25#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
26#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10 26#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
27 27
28static bool no_fw_recovery; 28bool no_fw_recovery;
29module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); 29module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
30MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery"); 30MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
31 31
32static bool no_fw_load = true; 32static bool no_fw_load = true;
33module_param(no_fw_load, bool, S_IRUGO | S_IWUSR); 33module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
@@ -191,17 +191,38 @@ static void wil_scan_timer_fn(ulong x)
191 schedule_work(&wil->fw_error_worker); 191 schedule_work(&wil->fw_error_worker);
192} 192}
193 193
194static int wil_wait_for_recovery(struct wil6210_priv *wil)
195{
196 if (wait_event_interruptible(wil->wq, wil->recovery_state !=
197 fw_recovery_pending)) {
198 wil_err(wil, "Interrupt, canceling recovery\n");
199 return -ERESTARTSYS;
200 }
201 if (wil->recovery_state != fw_recovery_running) {
202 wil_info(wil, "Recovery cancelled\n");
203 return -EINTR;
204 }
205 wil_info(wil, "Proceed with recovery\n");
206 return 0;
207}
208
209void wil_set_recovery_state(struct wil6210_priv *wil, int state)
210{
211 wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__,
212 wil->recovery_state, state);
213
214 wil->recovery_state = state;
215 wake_up_interruptible(&wil->wq);
216}
217
194static void wil_fw_error_worker(struct work_struct *work) 218static void wil_fw_error_worker(struct work_struct *work)
195{ 219{
196 struct wil6210_priv *wil = container_of(work, 220 struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
197 struct wil6210_priv, fw_error_worker); 221 fw_error_worker);
198 struct wireless_dev *wdev = wil->wdev; 222 struct wireless_dev *wdev = wil->wdev;
199 223
200 wil_dbg_misc(wil, "fw error worker\n"); 224 wil_dbg_misc(wil, "fw error worker\n");
201 225
202 if (no_fw_recovery)
203 return;
204
205 /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO 226 /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
206 * passed since last recovery attempt 227 * passed since last recovery attempt
207 */ 228 */
@@ -224,8 +245,13 @@ static void wil_fw_error_worker(struct work_struct *work)
224 case NL80211_IFTYPE_STATION: 245 case NL80211_IFTYPE_STATION:
225 case NL80211_IFTYPE_P2P_CLIENT: 246 case NL80211_IFTYPE_P2P_CLIENT:
226 case NL80211_IFTYPE_MONITOR: 247 case NL80211_IFTYPE_MONITOR:
227 wil_info(wil, "fw error recovery started (try %d)...\n", 248 wil_info(wil, "fw error recovery requested (try %d)...\n",
228 wil->recovery_count); 249 wil->recovery_count);
250 if (!no_fw_recovery)
251 wil->recovery_state = fw_recovery_running;
252 if (0 != wil_wait_for_recovery(wil))
253 break;
254
229 __wil_down(wil); 255 __wil_down(wil);
230 __wil_up(wil); 256 __wil_up(wil);
231 break; 257 break;
@@ -302,6 +328,7 @@ int wil_priv_init(struct wil6210_priv *wil)
302 328
303 INIT_LIST_HEAD(&wil->pending_wmi_ev); 329 INIT_LIST_HEAD(&wil->pending_wmi_ev);
304 spin_lock_init(&wil->wmi_ev_lock); 330 spin_lock_init(&wil->wmi_ev_lock);
331 init_waitqueue_head(&wil->wq);
305 332
306 wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); 333 wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
307 if (!wil->wmi_wq) 334 if (!wil->wmi_wq)
@@ -331,6 +358,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
331{ 358{
332 wil_dbg_misc(wil, "%s()\n", __func__); 359 wil_dbg_misc(wil, "%s()\n", __func__);
333 360
361 wil_set_recovery_state(wil, fw_recovery_idle);
334 del_timer_sync(&wil->scan_timer); 362 del_timer_sync(&wil->scan_timer);
335 cancel_work_sync(&wil->disconnect_worker); 363 cancel_work_sync(&wil->disconnect_worker);
336 cancel_work_sync(&wil->fw_error_worker); 364 cancel_work_sync(&wil->fw_error_worker);
@@ -573,6 +601,7 @@ int wil_reset(struct wil6210_priv *wil)
573void wil_fw_error_recovery(struct wil6210_priv *wil) 601void wil_fw_error_recovery(struct wil6210_priv *wil)
574{ 602{
575 wil_dbg_misc(wil, "starting fw error recovery\n"); 603 wil_dbg_misc(wil, "starting fw error recovery\n");
604 wil->recovery_state = fw_recovery_pending;
576 schedule_work(&wil->fw_error_worker); 605 schedule_work(&wil->fw_error_worker);
577} 606}
578 607
@@ -724,6 +753,7 @@ int wil_down(struct wil6210_priv *wil)
724 753
725 wil_dbg_misc(wil, "%s()\n", __func__); 754 wil_dbg_misc(wil, "%s()\n", __func__);
726 755
756 wil_set_recovery_state(wil, fw_recovery_idle);
727 mutex_lock(&wil->mutex); 757 mutex_lock(&wil->mutex);
728 rc = __wil_down(wil); 758 rc = __wil_down(wil);
729 mutex_unlock(&wil->mutex); 759 mutex_unlock(&wil->mutex);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 00c4f0a71fb8..2991609885f7 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -23,6 +23,7 @@
23#include <linux/timex.h> 23#include <linux/timex.h>
24#include "wil_platform.h" 24#include "wil_platform.h"
25 25
26extern bool no_fw_recovery;
26 27
27#define WIL_NAME "wil6210" 28#define WIL_NAME "wil6210"
28#define WIL_FW_NAME "wil6210.fw" 29#define WIL_FW_NAME "wil6210.fw"
@@ -379,6 +380,12 @@ struct wil_sta_info {
379 unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)]; 380 unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
380}; 381};
381 382
383enum {
384 fw_recovery_idle = 0,
385 fw_recovery_pending = 1,
386 fw_recovery_running = 2,
387};
388
382struct wil6210_priv { 389struct wil6210_priv {
383 struct pci_dev *pdev; 390 struct pci_dev *pdev;
384 int n_msi; 391 int n_msi;
@@ -389,8 +396,10 @@ struct wil6210_priv {
389 u32 hw_version; 396 u32 hw_version;
390 struct wil_board *board; 397 struct wil_board *board;
391 u8 n_mids; /* number of additional MIDs as reported by FW */ 398 u8 n_mids; /* number of additional MIDs as reported by FW */
392 int recovery_count; /* num of FW recovery attempts in a short time */ 399 u32 recovery_count; /* num of FW recovery attempts in a short time */
400 u32 recovery_state; /* FW recovery state machine */
393 unsigned long last_fw_recovery; /* jiffies of last fw recovery */ 401 unsigned long last_fw_recovery; /* jiffies of last fw recovery */
402 wait_queue_head_t wq; /* for all wait_event() use */
394 /* profile */ 403 /* profile */
395 u32 monitor_flags; 404 u32 monitor_flags;
396 u32 secure_pcp; /* create secure PCP? */ 405 u32 secure_pcp; /* create secure PCP? */
@@ -507,6 +516,7 @@ void wil_priv_deinit(struct wil6210_priv *wil);
507int wil_reset(struct wil6210_priv *wil); 516int wil_reset(struct wil6210_priv *wil);
508void wil_set_itr_trsh(struct wil6210_priv *wil); 517void wil_set_itr_trsh(struct wil6210_priv *wil);
509void wil_fw_error_recovery(struct wil6210_priv *wil); 518void wil_fw_error_recovery(struct wil6210_priv *wil);
519void wil_set_recovery_state(struct wil6210_priv *wil, int state);
510void wil_link_on(struct wil6210_priv *wil); 520void wil_link_on(struct wil6210_priv *wil);
511void wil_link_off(struct wil6210_priv *wil); 521void wil_link_off(struct wil6210_priv *wil);
512int wil_up(struct wil6210_priv *wil); 522int wil_up(struct wil6210_priv *wil);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index bd781c7adf2a..4311df982c60 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -299,6 +299,7 @@ static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
299{ 299{
300 wil_dbg_wmi(wil, "WMI: got FW ready event\n"); 300 wil_dbg_wmi(wil, "WMI: got FW ready event\n");
301 301
302 wil_set_recovery_state(wil, fw_recovery_idle);
302 set_bit(wil_status_fwready, &wil->status); 303 set_bit(wil_status_fwready, &wil->status);
303 /* let the reset sequence continue */ 304 /* let the reset sequence continue */
304 complete(&wil->wmi_ready); 305 complete(&wil->wmi_ready);