@@ -3624,9 +3624,14 @@ enum ieee80211_reconfig_type {
* @get_ftm_responder_stats: Retrieve FTM responder statistics, if available.
* Statistics should be cumulative, currently no way to reset is provided.
*
- * @set_noack_tid_bitmap: Set NoAck policy TID bitmap for a virtual interface.
- * Drivers mplementing this callback must take care of setting NoAck policy
- * in QOS control field based on the configured TID bitmap.
+ * @set_noack_tid_bitmap: Set NoAck policy TID bitmap. Apply the TID NoAck
+ * configuration for a particular station when @sta is non-NULL. NoAck
+ * policy is set to default for a peer when noack_map is -1 for the peer.
+ * The default NoAck policy for a peer is using netdev NoAck policy.
+ * When @sta is NULL, apply TID NoAck configuration at virtual interface
+ * level. Drivers mplementing this callback must take care of setting NoAck
+ * policy in QOS control field based on the configured TID bitmap.
+ * This callback may sleep.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -3917,7 +3922,8 @@ struct ieee80211_ops {
struct cfg80211_ftm_responder_stats *ftm_stats);
int (*set_noack_tid_bitmap)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, int noack_map);
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, int noack_map);
};
/**
@@ -344,18 +344,51 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
int noack_map)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct sta_info *sta;
+ int ret;
- sdata->noack_map = noack_map;
+ if (!peer) {
+ sdata->noack_map = noack_map;
- if (!sdata->local->ops->set_noack_tid_bitmap) {
- ieee80211_check_fast_xmit_iface(sdata);
- return 0;
+ if (!sdata->local->ops->set_noack_tid_bitmap) {
+ ieee80211_check_fast_xmit_iface(sdata);
+ return 0;
+ }
+
+ if (!ieee80211_sdata_running(sdata))
+ return 0;
+
+ return drv_set_noack_tid_bitmap(sdata->local, sdata, NULL,
+ noack_map);
}
+ /* NoAck policy is for a connected client on the dev */
+
if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
+ mutex_lock(&sdata->local->sta_mtx);
+
+ sta = sta_info_get_bss(sdata, peer);
+ if (!sta) {
+ mutex_unlock(&sdata->local->sta_mtx);
+ return -ENOENT;
+ }
+
+ sta->noack_map = noack_map;
+
+ if (!sdata->local->ops->set_noack_tid_bitmap) {
+ ieee80211_check_fast_xmit(sta);
+ mutex_unlock(&sdata->local->sta_mtx);
return 0;
+ }
+
+ ret = drv_set_noack_tid_bitmap(sdata->local, sdata,
+ &sta->sta, noack_map);
- return drv_set_noack_tid_bitmap(sdata->local, sdata, noack_map);
+ mutex_unlock(&sdata->local->sta_mtx);
+
+ return ret;
}
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
@@ -1280,6 +1280,7 @@ static inline void drv_del_nan_func(struct ieee80211_local *local,
static inline int drv_set_noack_tid_bitmap(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
int noack_map)
{
int ret;
@@ -1293,7 +1294,7 @@ static inline int drv_set_noack_tid_bitmap(struct ieee80211_local *local,
trace_drv_set_noack_tid_bitmap(local, sdata, noack_map);
ret = local->ops->set_noack_tid_bitmap(&local->hw, &sdata->vif,
- noack_map);
+ sta, noack_map);
trace_drv_return_int(local, ret);
return ret;
@@ -635,7 +635,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
goto err_del_interface;
if (sdata->noack_map)
- drv_set_noack_tid_bitmap(local, sdata,
+ drv_set_noack_tid_bitmap(local, sdata, NULL,
sdata->noack_map);
}
@@ -606,6 +606,8 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
kfree(sinfo);
+ sta->noack_map = -1;
+
sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr);
/* move reference to rcu-protected */
@@ -583,6 +583,9 @@ struct sta_info {
struct cfg80211_chan_def tdls_chandef;
+ /* TID bitmap for station's NoAck policy */
+ int noack_map;
+
/* keep last! */
struct ieee80211_sta sta;
};
@@ -2872,7 +2872,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
test_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT))
goto out;
- if (sdata->noack_map && !local->ops->set_noack_tid_bitmap)
+ if (((sta->noack_map == -1 && sdata->noack_map) ||
+ (sta->noack_map != -1 && sta->noack_map)) &&
+ !local->ops->set_noack_tid_bitmap)
goto out;
/* fast-xmit doesn't handle fragmentation at all */
@@ -227,6 +227,38 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
}
/**
+ * ieee80211_get_noack_map - Get TID bitmap of NoAck policy. NoAck policy
+ * could be device wide or per-station.
+ *
+ * @sdata: local subif
+ * @mac: MAC address of the receiver
+ */
+int ieee80211_get_noack_map(struct ieee80211_sub_if_data *sdata, const u8 *mac)
+{
+ struct sta_info *sta;
+ int noack_map = 0;
+
+ /* Retrieve per-station noack_map config for the receiver, if any */
+
+ rcu_read_lock();
+
+ sta = sta_info_get(sdata, mac);
+ if (!sta) {
+ rcu_read_unlock();
+ return noack_map;
+ }
+
+ noack_map = sta->noack_map;
+
+ rcu_read_unlock();
+
+ if (noack_map == -1)
+ noack_map = sdata->noack_map;
+
+ return noack_map;
+}
+
+/**
* ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
*
* @sdata: local subif
@@ -257,7 +289,7 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(hdr->addr1) ||
(!sdata->local->ops->set_noack_tid_bitmap &&
- sdata->noack_map & BIT(tid))) {
+ ieee80211_get_noack_map(sdata, hdr->addr1) & BIT(tid))) {
flags |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
}