|
@@ -2723,24 +2723,6 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
if (ret < 0)
|
|
|
wl1271_warning("rate policy for channel "
|
|
|
"failed %d", ret);
|
|
|
-
|
|
|
- /*
|
|
|
- * change the ROC channel. do it only if we are
|
|
|
- * not idle. otherwise, CROC will be called
|
|
|
- * anyway.
|
|
|
- */
|
|
|
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
|
|
|
- &wlvif->flags) &&
|
|
|
- wl12xx_dev_role_started(wlvif) &&
|
|
|
- !(conf->flags & IEEE80211_CONF_IDLE)) {
|
|
|
- ret = wl12xx_stop_dev(wl, wlvif);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
- ret = wl12xx_start_dev(wl, wlvif);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -4057,7 +4039,8 @@ sta_not_found:
|
|
|
|
|
|
/* ROC until connected (after EAPOL exchange) */
|
|
|
if (!is_ibss) {
|
|
|
- ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
|
|
|
+ ret = wl12xx_roc(wl, wlvif, wlvif->role_id,
|
|
|
+ wlvif->band, wlvif->channel);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
}
|
|
@@ -4692,6 +4675,134 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
|
|
|
wl1271_tx_flush(wl);
|
|
|
}
|
|
|
|
|
|
+static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
+ enum nl80211_channel_type channel_type,
|
|
|
+ int duration)
|
|
|
+{
|
|
|
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
+ struct wl1271 *wl = hw->priv;
|
|
|
+ int channel, ret = 0;
|
|
|
+
|
|
|
+ channel = ieee80211_frequency_to_channel(chan->center_freq);
|
|
|
+
|
|
|
+ wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
|
|
|
+ channel, wlvif->role_id);
|
|
|
+
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* return EBUSY if we can't ROC right now */
|
|
|
+ if (WARN_ON(wl->roc_vif ||
|
|
|
+ find_first_bit(wl->roc_map,
|
|
|
+ WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_ps_elp_wakeup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_sleep;
|
|
|
+
|
|
|
+ wl->roc_vif = vif;
|
|
|
+ ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
|
|
|
+ msecs_to_jiffies(duration));
|
|
|
+out_sleep:
|
|
|
+ wl1271_ps_elp_sleep(wl);
|
|
|
+out:
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int __wlcore_roc_completed(struct wl1271 *wl)
|
|
|
+{
|
|
|
+ struct wl12xx_vif *wlvif;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* already completed */
|
|
|
+ if (unlikely(!wl->roc_vif))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ wlvif = wl12xx_vif_to_data(wl->roc_vif);
|
|
|
+
|
|
|
+ if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ ret = wl12xx_stop_dev(wl, wlvif);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ wl->roc_vif = NULL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int wlcore_roc_completed(struct wl1271 *wl)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ wl1271_debug(DEBUG_MAC80211, "roc complete");
|
|
|
+
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_ps_elp_wakeup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = __wlcore_roc_completed(wl);
|
|
|
+
|
|
|
+ wl1271_ps_elp_sleep(wl);
|
|
|
+out:
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void wlcore_roc_complete_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct delayed_work *dwork;
|
|
|
+ struct wl1271 *wl;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ dwork = container_of(work, struct delayed_work, work);
|
|
|
+ wl = container_of(dwork, struct wl1271, roc_complete_work);
|
|
|
+
|
|
|
+ ret = wlcore_roc_completed(wl);
|
|
|
+ if (!ret)
|
|
|
+ ieee80211_remain_on_channel_expired(wl->hw);
|
|
|
+}
|
|
|
+
|
|
|
+static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
|
|
|
+{
|
|
|
+ struct wl1271 *wl = hw->priv;
|
|
|
+
|
|
|
+ wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
|
|
|
+
|
|
|
+ /* TODO: per-vif */
|
|
|
+ wl1271_tx_flush(wl);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * we can't just flush_work here, because it might deadlock
|
|
|
+ * (as we might get called from the same workqueue)
|
|
|
+ */
|
|
|
+ cancel_delayed_work_sync(&wl->roc_complete_work);
|
|
|
+ wlcore_roc_completed(wl);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
|
|
|
{
|
|
|
struct wl1271 *wl = hw->priv;
|
|
@@ -4883,6 +4994,8 @@ static const struct ieee80211_ops wl1271_ops = {
|
|
|
.set_bitrate_mask = wl12xx_set_bitrate_mask,
|
|
|
.channel_switch = wl12xx_op_channel_switch,
|
|
|
.flush = wlcore_op_flush,
|
|
|
+ .remain_on_channel = wlcore_op_remain_on_channel,
|
|
|
+ .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
|
|
|
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
|
|
|
};
|
|
|
|
|
@@ -5279,6 +5392,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
|
wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
|
|
|
sizeof(struct ieee80211_header);
|
|
|
|
|
|
+ wl->hw->wiphy->max_remain_on_channel_duration = 5000;
|
|
|
+
|
|
|
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
|
|
|
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
|
|
|
|
@@ -5377,6 +5492,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
|
|
|
INIT_WORK(&wl->tx_work, wl1271_tx_work);
|
|
|
INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
|
|
|
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
|
|
|
+ INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
|
|
|
INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
|
|
|
INIT_DELAYED_WORK(&wl->connection_loss_work,
|
|
|
wl1271_connection_loss_work);
|