|
@@ -1062,7 +1062,8 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
|
|
|
static const char* const PLT_MODE[] = {
|
|
|
"PLT_OFF",
|
|
|
"PLT_ON",
|
|
|
- "PLT_FEM_DETECT"
|
|
|
+ "PLT_FEM_DETECT",
|
|
|
+ "PLT_CHIP_AWAKE"
|
|
|
};
|
|
|
|
|
|
int ret;
|
|
@@ -1088,9 +1089,11 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
|
|
|
if (ret < 0)
|
|
|
goto power_off;
|
|
|
|
|
|
- ret = wl->ops->plt_init(wl);
|
|
|
- if (ret < 0)
|
|
|
- goto power_off;
|
|
|
+ if (plt_mode != PLT_CHIP_AWAKE) {
|
|
|
+ ret = wl->ops->plt_init(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto power_off;
|
|
|
+ }
|
|
|
|
|
|
wl->state = WLCORE_STATE_ON;
|
|
|
wl1271_notice("firmware booted in PLT mode %s (%s)",
|
|
@@ -2008,6 +2011,47 @@ out:
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
}
|
|
|
|
|
|
+static void wlcore_pending_auth_complete_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct delayed_work *dwork;
|
|
|
+ struct wl1271 *wl;
|
|
|
+ struct wl12xx_vif *wlvif;
|
|
|
+ unsigned long time_spare;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ dwork = container_of(work, struct delayed_work, work);
|
|
|
+ wlvif = container_of(dwork, struct wl12xx_vif,
|
|
|
+ pending_auth_complete_work);
|
|
|
+ wl = wlvif->wl;
|
|
|
+
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Make sure a second really passed since the last auth reply. Maybe
|
|
|
+ * a second auth reply arrived while we were stuck on the mutex.
|
|
|
+ * Check for a little less than the timeout to protect from scheduler
|
|
|
+ * irregularities.
|
|
|
+ */
|
|
|
+ time_spare = jiffies +
|
|
|
+ msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
|
|
|
+ if (!time_after(time_spare, wlvif->pending_auth_reply_time))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = wl1271_ps_elp_wakeup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* cancel the ROC if active */
|
|
|
+ wlcore_update_inconn_sta(wl, wlvif, NULL, false);
|
|
|
+
|
|
|
+ wl1271_ps_elp_sleep(wl);
|
|
|
+out:
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+}
|
|
|
+
|
|
|
static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
|
|
|
{
|
|
|
u8 policy = find_first_zero_bit(wl->rate_policies_map,
|
|
@@ -2159,6 +2203,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
|
|
|
wlcore_channel_switch_work);
|
|
|
INIT_DELAYED_WORK(&wlvif->connection_loss_work,
|
|
|
wlcore_connection_loss_work);
|
|
|
+ INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
|
|
|
+ wlcore_pending_auth_complete_work);
|
|
|
INIT_LIST_HEAD(&wlvif->list);
|
|
|
|
|
|
setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
|
|
@@ -2376,6 +2422,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
|
|
int ret = 0;
|
|
|
u8 role_type;
|
|
|
|
|
|
+ if (wl->plt) {
|
|
|
+ wl1271_error("Adding Interface not allowed while in PLT mode");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
|
|
|
IEEE80211_VIF_SUPPORTS_CQM_RSSI;
|
|
|
|
|
@@ -2590,6 +2641,7 @@ unlock:
|
|
|
cancel_work_sync(&wlvif->rx_streaming_disable_work);
|
|
|
cancel_delayed_work_sync(&wlvif->connection_loss_work);
|
|
|
cancel_delayed_work_sync(&wlvif->channel_switch_work);
|
|
|
+ cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
}
|
|
@@ -2875,6 +2927,25 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
|
wlvif->rate_set = wlvif->basic_rate_set;
|
|
|
}
|
|
|
|
|
|
+static void wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
+ bool idle)
|
|
|
+{
|
|
|
+ bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
|
|
|
+
|
|
|
+ if (idle == cur_idle)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (idle) {
|
|
|
+ clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
|
|
|
+ } else {
|
|
|
+ /* The current firmware only supports sched_scan in idle */
|
|
|
+ if (wl->sched_vif == wlvif)
|
|
|
+ wl->ops->sched_scan_stop(wl, wlvif);
|
|
|
+
|
|
|
+ set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
struct ieee80211_conf *conf, u32 changed)
|
|
|
{
|
|
@@ -3969,6 +4040,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
|
|
|
}
|
|
|
} else {
|
|
|
if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
|
|
|
+ /*
|
|
|
+ * AP might be in ROC in case we have just
|
|
|
+ * sent auth reply. handle it.
|
|
|
+ */
|
|
|
+ if (test_bit(wlvif->role_id, wl->roc_map))
|
|
|
+ wl12xx_croc(wl, wlvif->role_id);
|
|
|
+
|
|
|
ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
@@ -4120,6 +4198,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
|
|
|
do_join = true;
|
|
|
}
|
|
|
|
|
|
+ if (changed & BSS_CHANGED_IDLE && !is_ibss)
|
|
|
+ wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
|
|
|
+
|
|
|
if (changed & BSS_CHANGED_CQM) {
|
|
|
bool enable = false;
|
|
|
if (bss_conf->cqm_rssi_thold)
|
|
@@ -4656,29 +4737,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl,
|
|
|
wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
|
|
|
}
|
|
|
|
|
|
-static void wlcore_update_inconn_sta(struct wl1271 *wl,
|
|
|
- struct wl12xx_vif *wlvif,
|
|
|
- struct wl1271_station *wl_sta,
|
|
|
- bool in_connection)
|
|
|
+/*
|
|
|
+ * when wl_sta is NULL, we treat this call as if coming from a
|
|
|
+ * pending auth reply.
|
|
|
+ * wl->mutex must be taken and the FW must be awake when the call
|
|
|
+ * takes place.
|
|
|
+ */
|
|
|
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
+ struct wl1271_station *wl_sta, bool in_conn)
|
|
|
{
|
|
|
- if (in_connection) {
|
|
|
- if (WARN_ON(wl_sta->in_connection))
|
|
|
+ if (in_conn) {
|
|
|
+ if (WARN_ON(wl_sta && wl_sta->in_connection))
|
|
|
return;
|
|
|
- wl_sta->in_connection = true;
|
|
|
- if (!wlvif->inconn_count++)
|
|
|
+
|
|
|
+ if (!wlvif->ap_pending_auth_reply &&
|
|
|
+ !wlvif->inconn_count)
|
|
|
wlcore_roc_if_possible(wl, wlvif);
|
|
|
+
|
|
|
+ if (wl_sta) {
|
|
|
+ wl_sta->in_connection = true;
|
|
|
+ wlvif->inconn_count++;
|
|
|
+ } else {
|
|
|
+ wlvif->ap_pending_auth_reply = true;
|
|
|
+ }
|
|
|
} else {
|
|
|
- if (!wl_sta->in_connection)
|
|
|
+ if (wl_sta && !wl_sta->in_connection)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
|
|
|
return;
|
|
|
|
|
|
- wl_sta->in_connection = false;
|
|
|
- wlvif->inconn_count--;
|
|
|
- if (WARN_ON(wlvif->inconn_count < 0))
|
|
|
+ if (WARN_ON(wl_sta && !wlvif->inconn_count))
|
|
|
return;
|
|
|
|
|
|
- if (!wlvif->inconn_count)
|
|
|
- if (test_bit(wlvif->role_id, wl->roc_map))
|
|
|
- wl12xx_croc(wl, wlvif->role_id);
|
|
|
+ if (wl_sta) {
|
|
|
+ wl_sta->in_connection = false;
|
|
|
+ wlvif->inconn_count--;
|
|
|
+ } else {
|
|
|
+ wlvif->ap_pending_auth_reply = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
|
|
|
+ test_bit(wlvif->role_id, wl->roc_map))
|
|
|
+ wl12xx_croc(wl, wlvif->role_id);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -5313,10 +5414,7 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
|
|
|
|
|
|
/* 5 GHz band channels for WL1273 */
|
|
|
static struct ieee80211_channel wl1271_channels_5ghz[] = {
|
|
|
- { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
|
|
|
{ .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
|
|
|
- { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
|
|
|
- { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
|
|
|
{ .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
|
|
|
{ .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
|
|
|
{ .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
|
|
@@ -5896,6 +5994,11 @@ static const struct wiphy_wowlan_support wlcore_wowlan_support = {
|
|
|
};
|
|
|
#endif
|
|
|
|
|
|
+static irqreturn_t wlcore_hardirq(int irq, void *cookie)
|
|
|
+{
|
|
|
+ return IRQ_WAKE_THREAD;
|
|
|
+}
|
|
|
+
|
|
|
static void wlcore_nvs_cb(const struct firmware *fw, void *context)
|
|
|
{
|
|
|
struct wl1271 *wl = context;
|
|
@@ -5904,6 +6007,7 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
|
|
|
struct wl12xx_platform_data *pdata = pdev_data->pdata;
|
|
|
unsigned long irqflags;
|
|
|
int ret;
|
|
|
+ irq_handler_t hardirq_fn = NULL;
|
|
|
|
|
|
if (fw) {
|
|
|
wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
|
|
@@ -5932,12 +6036,14 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
|
|
|
wl->platform_quirks = pdata->platform_quirks;
|
|
|
wl->if_ops = pdev_data->if_ops;
|
|
|
|
|
|
- if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
|
|
|
+ if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) {
|
|
|
irqflags = IRQF_TRIGGER_RISING;
|
|
|
- else
|
|
|
+ hardirq_fn = wlcore_hardirq;
|
|
|
+ } else {
|
|
|
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
|
|
|
+ }
|
|
|
|
|
|
- ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
|
|
|
+ ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq,
|
|
|
irqflags, pdev->name, wl);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("request_irq() failed: %d", ret);
|