wl1271: Add handling for failing hardware scan command

Currently, the driver does not handle a failing hardware command to scan in
any way - effectively, the scan machine will jam until the driver is shut down,
and future scan requests will just return -EBUSY to user space, resulting in
a type of busy-loop. The same problem occurs if the firmware fails to deliver
the scan completion event - add timeout for this.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 3576c1c..cae4893 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -296,6 +296,7 @@
 struct wl1271_scan {
 	struct cfg80211_scan_request *req;
 	bool *scanned_ch;
+	bool failed;
 	u8 state;
 	u8 ssid[IW_ESSID_MAX_SIZE+1];
 	size_t ssid_len;
@@ -419,7 +420,7 @@
 
 	/* Are we currently scanning */
 	struct wl1271_scan scan;
-	struct work_struct scan_complete_work;
+	struct delayed_work scan_complete_work;
 
 	/* Our association ID */
 	u16 aid;
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index fecb0c3..c131758 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -657,8 +657,8 @@
 
 	INIT_WORK(&wl->irq_work, wl1271_irq_work);
 	INIT_WORK(&wl->tx_work, wl1271_tx_work);
-	INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
 	INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
+	INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
 
 	return 0;
 }
@@ -1013,7 +1013,7 @@
 
 	mutex_unlock(&wl->mutex);
 
-	cancel_work_sync(&wl->scan_complete_work);
+	cancel_delayed_work_sync(&wl->scan_complete_work);
 	cancel_work_sync(&wl->irq_work);
 	cancel_work_sync(&wl->tx_work);
 	cancel_delayed_work_sync(&wl->pspoll_work);
diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c
index 20caceb..37f9ccb 100644
--- a/drivers/net/wireless/wl12xx/wl1271_scan.c
+++ b/drivers/net/wireless/wl12xx/wl1271_scan.c
@@ -30,8 +30,11 @@
 
 void wl1271_scan_complete_work(struct work_struct *work)
 {
-	struct wl1271 *wl =
-		container_of(work, struct wl1271, scan_complete_work);
+	struct delayed_work *dwork;
+	struct wl1271 *wl;
+
+	dwork = container_of(work, struct delayed_work, work);
+	wl = container_of(dwork, struct wl1271, scan_complete_work);
 
 	wl1271_debug(DEBUG_SCAN, "Scanning complete");
 
@@ -48,6 +51,11 @@
 	mutex_unlock(&wl->mutex);
 
 	ieee80211_scan_completed(wl->hw, false);
+
+	if (wl->scan.failed) {
+		wl1271_info("Scan completed due to error.");
+		ieee80211_queue_work(wl->hw, &wl->recovery_work);
+	}
 }
 
 
@@ -191,7 +199,7 @@
 
 void wl1271_scan_stm(struct wl1271 *wl)
 {
-	int ret;
+	int ret = 0;
 
 	switch (wl->scan.state) {
 	case WL1271_SCAN_STATE_IDLE:
@@ -241,13 +249,22 @@
 		break;
 
 	case WL1271_SCAN_STATE_DONE:
-		ieee80211_queue_work(wl->hw, &wl->scan_complete_work);
+		wl->scan.failed = false;
+		cancel_delayed_work(&wl->scan_complete_work);
+		ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+					     msecs_to_jiffies(0));
 		break;
 
 	default:
 		wl1271_error("invalid scan state");
 		break;
 	}
+
+	if (ret < 0) {
+		cancel_delayed_work(&wl->scan_complete_work);
+		ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+					     msecs_to_jiffies(0));
+	}
 }
 
 int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
@@ -270,6 +287,11 @@
 	wl->scan.scanned_ch = kzalloc(req->n_channels *
 				      sizeof(*wl->scan.scanned_ch),
 				      GFP_KERNEL);
+	/* we assume failure so that timeout scenarios are handled correctly */
+	wl->scan.failed = true;
+	ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+				     msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
+
 	wl1271_scan_stm(wl);
 
 	return 0;
diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.h b/drivers/net/wireless/wl12xx/wl1271_scan.h
index 1404e00..bb7af2a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_scan.h
+++ b/drivers/net/wireless/wl12xx/wl1271_scan.h
@@ -46,6 +46,8 @@
 #define WL1271_SCAN_BAND_5_GHZ 1
 #define WL1271_SCAN_PROBE_REQS 3
 
+#define WL1271_SCAN_TIMEOUT    10000 /* msec */
+
 enum {
 	WL1271_SCAN_STATE_IDLE,
 	WL1271_SCAN_STATE_2GHZ_ACTIVE,