|
@@ -1606,6 +1606,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
|
mutex_unlock(&sdata->local->mtx);
|
|
mutex_unlock(&sdata->local->mtx);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool
|
|
|
|
+__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
+ bool ret;
|
|
|
|
+ int ac;
|
|
|
|
+
|
|
|
|
+ if (local->hw.queues < IEEE80211_NUM_ACS)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
|
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
|
|
|
+ int non_acm_ac;
|
|
|
|
+ unsigned long now = jiffies;
|
|
|
|
+
|
|
|
|
+ if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
|
|
|
|
+ tx_tspec->admitted_time &&
|
|
|
|
+ time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
+ tx_tspec->consumed_tx_time = 0;
|
|
|
|
+ tx_tspec->time_slice_start = now;
|
|
|
|
+
|
|
|
|
+ if (tx_tspec->downgraded)
|
|
|
|
+ tx_tspec->action =
|
|
|
|
+ TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (tx_tspec->action) {
|
|
|
|
+ case TX_TSPEC_ACTION_STOP_DOWNGRADE:
|
|
|
|
+ /* take the original parameters */
|
|
|
|
+ if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
|
|
|
|
+ sdata_err(sdata,
|
|
|
|
+ "failed to set TX queue parameters for queue %d\n",
|
|
|
|
+ ac);
|
|
|
|
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
+ tx_tspec->downgraded = false;
|
|
|
|
+ ret = true;
|
|
|
|
+ break;
|
|
|
|
+ case TX_TSPEC_ACTION_DOWNGRADE:
|
|
|
|
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
+ ret = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ /* downgrade next lower non-ACM AC */
|
|
|
|
+ for (non_acm_ac = ac + 1;
|
|
|
|
+ non_acm_ac < IEEE80211_NUM_ACS;
|
|
|
|
+ non_acm_ac++)
|
|
|
|
+ if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
|
|
|
|
+ break;
|
|
|
|
+ /* The loop will result in using BK even if it requires
|
|
|
|
+ * admission control, such configuration makes no sense
|
|
|
|
+ * and we have to transmit somehow - the AC selection
|
|
|
|
+ * does the same thing.
|
|
|
|
+ */
|
|
|
|
+ if (drv_conf_tx(local, sdata, ac,
|
|
|
|
+ &sdata->tx_conf[non_acm_ac]))
|
|
|
|
+ sdata_err(sdata,
|
|
|
|
+ "failed to set TX queue parameters for queue %d\n",
|
|
|
|
+ ac);
|
|
|
|
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
+ ret = true;
|
|
|
|
+ schedule_delayed_work(&ifmgd->tx_tspec_wk,
|
|
|
|
+ tx_tspec->time_slice_start + HZ - now + 1);
|
|
|
|
+ break;
|
|
|
|
+ case TX_TSPEC_ACTION_NONE:
|
|
|
|
+ /* nothing now */
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
|
|
|
+{
|
|
|
|
+ if (__ieee80211_sta_handle_tspec_ac_params(sdata))
|
|
|
|
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_sub_if_data *sdata;
|
|
|
|
+
|
|
|
|
+ sdata = container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
+ u.mgd.tx_tspec_wk.work);
|
|
|
|
+ ieee80211_sta_handle_tspec_ac_params(sdata);
|
|
|
|
+}
|
|
|
|
+
|
|
/* MLME */
|
|
/* MLME */
|
|
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
|
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
|
struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_sub_if_data *sdata,
|
|
@@ -1690,12 +1779,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
|
params.uapsd = uapsd;
|
|
params.uapsd = uapsd;
|
|
|
|
|
|
mlme_dbg(sdata,
|
|
mlme_dbg(sdata,
|
|
- "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
|
|
|
|
|
|
+ "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
|
|
queue, aci, acm,
|
|
queue, aci, acm,
|
|
params.aifs, params.cw_min, params.cw_max,
|
|
params.aifs, params.cw_min, params.cw_max,
|
|
- params.txop, params.uapsd);
|
|
|
|
|
|
+ params.txop, params.uapsd,
|
|
|
|
+ ifmgd->tx_tspec[queue].downgraded);
|
|
sdata->tx_conf[queue] = params;
|
|
sdata->tx_conf[queue] = params;
|
|
- if (drv_conf_tx(local, sdata, queue, ¶ms))
|
|
|
|
|
|
+ if (!ifmgd->tx_tspec[queue].downgraded &&
|
|
|
|
+ drv_conf_tx(local, sdata, queue, ¶ms))
|
|
sdata_err(sdata,
|
|
sdata_err(sdata,
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
queue);
|
|
queue);
|
|
@@ -1958,6 +2049,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
}
|
|
}
|
|
mutex_unlock(&local->mtx);
|
|
mutex_unlock(&local->mtx);
|
|
|
|
|
|
|
|
+ /* existing TX TSPEC sessions no longer exist */
|
|
|
|
+ memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
|
|
|
|
+ cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
|
|
|
|
+
|
|
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
|
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2010,9 +2105,46 @@ out:
|
|
mutex_unlock(&local->mtx);
|
|
mutex_unlock(&local->mtx);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
|
|
|
|
+ struct ieee80211_hdr *hdr,
|
|
|
|
+ u16 tx_time)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
+ u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
|
|
|
|
+ int ac = ieee80211_ac_from_tid(tid);
|
|
|
|
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
|
|
|
+ unsigned long now = jiffies;
|
|
|
|
+
|
|
|
|
+ if (likely(!tx_tspec->admitted_time))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
+ tx_tspec->consumed_tx_time = 0;
|
|
|
|
+ tx_tspec->time_slice_start = now;
|
|
|
|
+
|
|
|
|
+ if (tx_tspec->downgraded) {
|
|
|
|
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
|
|
|
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (tx_tspec->downgraded)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ tx_tspec->consumed_tx_time += tx_time;
|
|
|
|
+
|
|
|
|
+ if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
|
|
|
|
+ tx_tspec->downgraded = true;
|
|
|
|
+ tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
|
|
|
|
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
|
- struct ieee80211_hdr *hdr, bool ack)
|
|
|
|
|
|
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
|
|
{
|
|
{
|
|
|
|
+ ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
|
|
|
|
+
|
|
if (!ieee80211_is_data(hdr->frame_control))
|
|
if (!ieee80211_is_data(hdr->frame_control))
|
|
return;
|
|
return;
|
|
|
|
|
|
@@ -3834,6 +3966,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
|
(unsigned long) sdata);
|
|
(unsigned long) sdata);
|
|
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
|
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
|
(unsigned long) sdata);
|
|
(unsigned long) sdata);
|
|
|
|
+ INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
|
|
|
|
+ ieee80211_sta_handle_tspec_ac_params_wk);
|
|
|
|
|
|
ifmgd->flags = 0;
|
|
ifmgd->flags = 0;
|
|
ifmgd->powersave = sdata->wdev.ps;
|
|
ifmgd->powersave = sdata->wdev.ps;
|