diff options
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-fw.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | 31 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 59 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sta.c | 171 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sta.h | 57 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/tx.c | 23 |
8 files changed, 270 insertions, 77 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 5f1493c44097..726327782401 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h | |||
@@ -95,6 +95,7 @@ | |||
95 | * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a | 95 | * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a |
96 | * single bound interface). | 96 | * single bound interface). |
97 | * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save | 97 | * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save |
98 | * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients | ||
98 | */ | 99 | */ |
99 | enum iwl_ucode_tlv_flag { | 100 | enum iwl_ucode_tlv_flag { |
100 | IWL_UCODE_TLV_FLAGS_PAN = BIT(0), | 101 | IWL_UCODE_TLV_FLAGS_PAN = BIT(0), |
@@ -119,6 +120,7 @@ enum iwl_ucode_tlv_flag { | |||
119 | IWL_UCODE_TLV_FLAGS_P2P_PS = BIT(21), | 120 | IWL_UCODE_TLV_FLAGS_P2P_PS = BIT(21), |
120 | IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), | 121 | IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), |
121 | IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), | 122 | IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), |
123 | IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30), | ||
122 | }; | 124 | }; |
123 | 125 | ||
124 | /* The default calibrate table size if not specified by firmware file */ | 126 | /* The default calibrate table size if not specified by firmware file */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 1b60fdff6a56..d63647867262 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | |||
@@ -199,11 +199,14 @@ enum iwl_sta_modify_flag { | |||
199 | * @STA_SLEEP_STATE_AWAKE: | 199 | * @STA_SLEEP_STATE_AWAKE: |
200 | * @STA_SLEEP_STATE_PS_POLL: | 200 | * @STA_SLEEP_STATE_PS_POLL: |
201 | * @STA_SLEEP_STATE_UAPSD: | 201 | * @STA_SLEEP_STATE_UAPSD: |
202 | * @STA_SLEEP_STATE_MOREDATA: set more-data bit on | ||
203 | * (last) released frame | ||
202 | */ | 204 | */ |
203 | enum iwl_sta_sleep_flag { | 205 | enum iwl_sta_sleep_flag { |
204 | STA_SLEEP_STATE_AWAKE = 0, | 206 | STA_SLEEP_STATE_AWAKE = 0, |
205 | STA_SLEEP_STATE_PS_POLL = BIT(0), | 207 | STA_SLEEP_STATE_PS_POLL = BIT(0), |
206 | STA_SLEEP_STATE_UAPSD = BIT(1), | 208 | STA_SLEEP_STATE_UAPSD = BIT(1), |
209 | STA_SLEEP_STATE_MOREDATA = BIT(2), | ||
207 | }; | 210 | }; |
208 | 211 | ||
209 | /* STA ID and color bits definitions */ | 212 | /* STA ID and color bits definitions */ |
@@ -318,13 +321,15 @@ struct iwl_mvm_add_sta_cmd_v5 { | |||
318 | } __packed; /* ADD_STA_CMD_API_S_VER_5 */ | 321 | } __packed; /* ADD_STA_CMD_API_S_VER_5 */ |
319 | 322 | ||
320 | /** | 323 | /** |
321 | * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station | 324 | * struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station |
322 | * VER_6 of this command is quite similar to VER_5 except | 325 | * VER_7 of this command is quite similar to VER_5 except |
323 | * exclusion of all fields related to the security key installation. | 326 | * exclusion of all fields related to the security key installation. |
327 | * It only differs from VER_6 by the "awake_acs" field that is | ||
328 | * reserved and ignored in VER_6. | ||
324 | */ | 329 | */ |
325 | struct iwl_mvm_add_sta_cmd_v6 { | 330 | struct iwl_mvm_add_sta_cmd_v7 { |
326 | u8 add_modify; | 331 | u8 add_modify; |
327 | u8 reserved1; | 332 | u8 awake_acs; |
328 | __le16 tid_disable_tx; | 333 | __le16 tid_disable_tx; |
329 | __le32 mac_id_n_color; | 334 | __le32 mac_id_n_color; |
330 | u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ | 335 | u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ |
@@ -342,7 +347,7 @@ struct iwl_mvm_add_sta_cmd_v6 { | |||
342 | __le16 assoc_id; | 347 | __le16 assoc_id; |
343 | __le16 beamform_flags; | 348 | __le16 beamform_flags; |
344 | __le32 tfd_queue_msk; | 349 | __le32 tfd_queue_msk; |
345 | } __packed; /* ADD_STA_CMD_API_S_VER_6 */ | 350 | } __packed; /* ADD_STA_CMD_API_S_VER_7 */ |
346 | 351 | ||
347 | /** | 352 | /** |
348 | * struct iwl_mvm_add_sta_key_cmd - add/modify sta key | 353 | * struct iwl_mvm_add_sta_key_cmd - add/modify sta key |
@@ -432,5 +437,15 @@ struct iwl_mvm_wep_key_cmd { | |||
432 | struct iwl_mvm_wep_key wep_key[0]; | 437 | struct iwl_mvm_wep_key wep_key[0]; |
433 | } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ | 438 | } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ |
434 | 439 | ||
440 | /** | ||
441 | * struct iwl_mvm_eosp_notification - EOSP notification from firmware | ||
442 | * @remain_frame_count: # of frames remaining, non-zero if SP was cut | ||
443 | * short by GO absence | ||
444 | * @sta_id: station ID | ||
445 | */ | ||
446 | struct iwl_mvm_eosp_notification { | ||
447 | __le32 remain_frame_count; | ||
448 | __le32 sta_id; | ||
449 | } __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ | ||
435 | 450 | ||
436 | #endif /* __fw_api_sta_h__ */ | 451 | #endif /* __fw_api_sta_h__ */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 989d7dbdca6c..a043a1f2f06f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h | |||
@@ -163,6 +163,7 @@ enum { | |||
163 | TX_ANT_CONFIGURATION_CMD = 0x98, | 163 | TX_ANT_CONFIGURATION_CMD = 0x98, |
164 | BT_CONFIG = 0x9b, | 164 | BT_CONFIG = 0x9b, |
165 | STATISTICS_NOTIFICATION = 0x9d, | 165 | STATISTICS_NOTIFICATION = 0x9d, |
166 | EOSP_NOTIFICATION = 0x9e, | ||
166 | REDUCE_TX_POWER_CMD = 0x9f, | 167 | REDUCE_TX_POWER_CMD = 0x9f, |
167 | 168 | ||
168 | /* RF-KILL commands and notifications */ | 169 | /* RF-KILL commands and notifications */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index c49b5073c251..5b9cfe1f35bd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c | |||
@@ -203,6 +203,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) | |||
203 | hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | | 203 | hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | |
204 | REGULATORY_DISABLE_BEACON_HINTS; | 204 | REGULATORY_DISABLE_BEACON_HINTS; |
205 | 205 | ||
206 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) | ||
207 | hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; | ||
208 | |||
206 | hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; | 209 | hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; |
207 | hw->wiphy->n_iface_combinations = | 210 | hw->wiphy->n_iface_combinations = |
208 | ARRAY_SIZE(iwl_mvm_iface_combinations); | 211 | ARRAY_SIZE(iwl_mvm_iface_combinations); |
@@ -305,6 +308,9 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, | |||
305 | struct sk_buff *skb) | 308 | struct sk_buff *skb) |
306 | { | 309 | { |
307 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 310 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
311 | struct ieee80211_sta *sta = control->sta; | ||
312 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
313 | struct ieee80211_hdr *hdr = (void *)skb->data; | ||
308 | 314 | ||
309 | if (iwl_mvm_is_radio_killed(mvm)) { | 315 | if (iwl_mvm_is_radio_killed(mvm)) { |
310 | IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); | 316 | IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); |
@@ -315,8 +321,16 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, | |||
315 | !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) | 321 | !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) |
316 | goto drop; | 322 | goto drop; |
317 | 323 | ||
318 | if (control->sta) { | 324 | /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ |
319 | if (iwl_mvm_tx_skb(mvm, skb, control->sta)) | 325 | if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && |
326 | ieee80211_is_mgmt(hdr->frame_control) && | ||
327 | !ieee80211_is_deauth(hdr->frame_control) && | ||
328 | !ieee80211_is_disassoc(hdr->frame_control) && | ||
329 | !ieee80211_is_action(hdr->frame_control))) | ||
330 | sta = NULL; | ||
331 | |||
332 | if (sta) { | ||
333 | if (iwl_mvm_tx_skb(mvm, skb, sta)) | ||
320 | goto drop; | 334 | goto drop; |
321 | return; | 335 | return; |
322 | } | 336 | } |
@@ -1168,20 +1182,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, | |||
1168 | 1182 | ||
1169 | static void | 1183 | static void |
1170 | iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, | 1184 | iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, |
1171 | struct ieee80211_sta *sta, u16 tid, | 1185 | struct ieee80211_sta *sta, u16 tids, |
1172 | int num_frames, | 1186 | int num_frames, |
1173 | enum ieee80211_frame_release_type reason, | 1187 | enum ieee80211_frame_release_type reason, |
1174 | bool more_data) | 1188 | bool more_data) |
1175 | { | 1189 | { |
1176 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 1190 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
1177 | 1191 | ||
1178 | /* TODO: how do we tell the fw to send frames for a specific TID */ | 1192 | /* Called when we need to transmit (a) frame(s) from mac80211 */ |
1179 | 1193 | ||
1180 | /* | 1194 | iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, |
1181 | * The fw will send EOSP notification when the last frame will be | 1195 | tids, more_data, false); |
1182 | * transmitted. | 1196 | } |
1183 | */ | 1197 | |
1184 | iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames); | 1198 | static void |
1199 | iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, | ||
1200 | struct ieee80211_sta *sta, u16 tids, | ||
1201 | int num_frames, | ||
1202 | enum ieee80211_frame_release_type reason, | ||
1203 | bool more_data) | ||
1204 | { | ||
1205 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | ||
1206 | |||
1207 | /* Called when we need to transmit (a) frame(s) from agg queue */ | ||
1208 | |||
1209 | iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, | ||
1210 | tids, more_data, true); | ||
1185 | } | 1211 | } |
1186 | 1212 | ||
1187 | static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, | 1213 | static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, |
@@ -1191,11 +1217,25 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, | |||
1191 | { | 1217 | { |
1192 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 1218 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
1193 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | 1219 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
1220 | int tid; | ||
1194 | 1221 | ||
1195 | switch (cmd) { | 1222 | switch (cmd) { |
1196 | case STA_NOTIFY_SLEEP: | 1223 | case STA_NOTIFY_SLEEP: |
1197 | if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) | 1224 | if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) |
1198 | ieee80211_sta_block_awake(hw, sta, true); | 1225 | ieee80211_sta_block_awake(hw, sta, true); |
1226 | spin_lock_bh(&mvmsta->lock); | ||
1227 | for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { | ||
1228 | struct iwl_mvm_tid_data *tid_data; | ||
1229 | |||
1230 | tid_data = &mvmsta->tid_data[tid]; | ||
1231 | if (tid_data->state != IWL_AGG_ON && | ||
1232 | tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) | ||
1233 | continue; | ||
1234 | if (iwl_mvm_tid_queued(tid_data) == 0) | ||
1235 | continue; | ||
1236 | ieee80211_sta_set_buffered(sta, tid, true); | ||
1237 | } | ||
1238 | spin_unlock_bh(&mvmsta->lock); | ||
1199 | /* | 1239 | /* |
1200 | * The fw updates the STA to be asleep. Tx packets on the Tx | 1240 | * The fw updates the STA to be asleep. Tx packets on the Tx |
1201 | * queues to this station will not be transmitted. The fw will | 1241 | * queues to this station will not be transmitted. The fw will |
@@ -1914,6 +1954,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = { | |||
1914 | .sta_state = iwl_mvm_mac_sta_state, | 1954 | .sta_state = iwl_mvm_mac_sta_state, |
1915 | .sta_notify = iwl_mvm_mac_sta_notify, | 1955 | .sta_notify = iwl_mvm_mac_sta_notify, |
1916 | .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, | 1956 | .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, |
1957 | .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, | ||
1917 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, | 1958 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, |
1918 | .sta_rc_update = iwl_mvm_sta_rc_update, | 1959 | .sta_rc_update = iwl_mvm_sta_rc_update, |
1919 | .conf_tx = iwl_mvm_mac_conf_tx, | 1960 | .conf_tx = iwl_mvm_mac_conf_tx, |
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index a3d43de342d7..a65eeb335cfb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c | |||
@@ -222,6 +222,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { | |||
222 | 222 | ||
223 | RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), | 223 | RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), |
224 | 224 | ||
225 | RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), | ||
226 | |||
225 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), | 227 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), |
226 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), | 228 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), |
227 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, | 229 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, |
@@ -284,6 +286,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { | |||
284 | CMD(BEACON_NOTIFICATION), | 286 | CMD(BEACON_NOTIFICATION), |
285 | CMD(BEACON_TEMPLATE_CMD), | 287 | CMD(BEACON_TEMPLATE_CMD), |
286 | CMD(STATISTICS_NOTIFICATION), | 288 | CMD(STATISTICS_NOTIFICATION), |
289 | CMD(EOSP_NOTIFICATION), | ||
287 | CMD(REDUCE_TX_POWER_CMD), | 290 | CMD(REDUCE_TX_POWER_CMD), |
288 | CMD(TX_ANT_CONFIGURATION_CMD), | 291 | CMD(TX_ANT_CONFIGURATION_CMD), |
289 | CMD(D3_CONFIG_CMD), | 292 | CMD(D3_CONFIG_CMD), |
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index ec1812133235..af94f75c3999 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c | |||
@@ -66,27 +66,27 @@ | |||
66 | #include "sta.h" | 66 | #include "sta.h" |
67 | #include "rs.h" | 67 | #include "rs.h" |
68 | 68 | ||
69 | static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6, | 69 | static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7, |
70 | struct iwl_mvm_add_sta_cmd_v5 *cmd_v5) | 70 | struct iwl_mvm_add_sta_cmd_v5 *cmd_v5) |
71 | { | 71 | { |
72 | memset(cmd_v5, 0, sizeof(*cmd_v5)); | 72 | memset(cmd_v5, 0, sizeof(*cmd_v5)); |
73 | 73 | ||
74 | cmd_v5->add_modify = cmd_v6->add_modify; | 74 | cmd_v5->add_modify = cmd_v7->add_modify; |
75 | cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; | 75 | cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx; |
76 | cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; | 76 | cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color; |
77 | memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN); | 77 | memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN); |
78 | cmd_v5->sta_id = cmd_v6->sta_id; | 78 | cmd_v5->sta_id = cmd_v7->sta_id; |
79 | cmd_v5->modify_mask = cmd_v6->modify_mask; | 79 | cmd_v5->modify_mask = cmd_v7->modify_mask; |
80 | cmd_v5->station_flags = cmd_v6->station_flags; | 80 | cmd_v5->station_flags = cmd_v7->station_flags; |
81 | cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; | 81 | cmd_v5->station_flags_msk = cmd_v7->station_flags_msk; |
82 | cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; | 82 | cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid; |
83 | cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; | 83 | cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid; |
84 | cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; | 84 | cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn; |
85 | cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; | 85 | cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count; |
86 | cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; | 86 | cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags; |
87 | cmd_v5->assoc_id = cmd_v6->assoc_id; | 87 | cmd_v5->assoc_id = cmd_v7->assoc_id; |
88 | cmd_v5->beamform_flags = cmd_v6->beamform_flags; | 88 | cmd_v5->beamform_flags = cmd_v7->beamform_flags; |
89 | cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; | 89 | cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk; |
90 | } | 90 | } |
91 | 91 | ||
92 | static void | 92 | static void |
@@ -110,7 +110,7 @@ iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd, | |||
110 | } | 110 | } |
111 | 111 | ||
112 | static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm, | 112 | static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm, |
113 | struct iwl_mvm_add_sta_cmd_v6 *cmd, | 113 | struct iwl_mvm_add_sta_cmd_v7 *cmd, |
114 | int *status) | 114 | int *status) |
115 | { | 115 | { |
116 | struct iwl_mvm_add_sta_cmd_v5 cmd_v5; | 116 | struct iwl_mvm_add_sta_cmd_v5 cmd_v5; |
@@ -119,14 +119,14 @@ static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm, | |||
119 | return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd), | 119 | return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd), |
120 | cmd, status); | 120 | cmd, status); |
121 | 121 | ||
122 | iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); | 122 | iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5); |
123 | 123 | ||
124 | return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5), | 124 | return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5), |
125 | &cmd_v5, status); | 125 | &cmd_v5, status); |
126 | } | 126 | } |
127 | 127 | ||
128 | static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags, | 128 | static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags, |
129 | struct iwl_mvm_add_sta_cmd_v6 *cmd) | 129 | struct iwl_mvm_add_sta_cmd_v7 *cmd) |
130 | { | 130 | { |
131 | struct iwl_mvm_add_sta_cmd_v5 cmd_v5; | 131 | struct iwl_mvm_add_sta_cmd_v5 cmd_v5; |
132 | 132 | ||
@@ -134,7 +134,7 @@ static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags, | |||
134 | return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, | 134 | return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, |
135 | sizeof(*cmd), cmd); | 135 | sizeof(*cmd), cmd); |
136 | 136 | ||
137 | iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); | 137 | iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5); |
138 | 138 | ||
139 | return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5), | 139 | return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5), |
140 | &cmd_v5); | 140 | &cmd_v5); |
@@ -196,7 +196,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
196 | bool update) | 196 | bool update) |
197 | { | 197 | { |
198 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; | 198 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; |
199 | struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd; | 199 | struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd; |
200 | int ret; | 200 | int ret; |
201 | u32 status; | 201 | u32 status; |
202 | u32 agg_size = 0, mpdu_dens = 0; | 202 | u32 agg_size = 0, mpdu_dens = 0; |
@@ -368,7 +368,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm, | |||
368 | int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, | 368 | int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, |
369 | bool drain) | 369 | bool drain) |
370 | { | 370 | { |
371 | struct iwl_mvm_add_sta_cmd_v6 cmd = {}; | 371 | struct iwl_mvm_add_sta_cmd_v7 cmd = {}; |
372 | int ret; | 372 | int ret; |
373 | u32 status; | 373 | u32 status; |
374 | 374 | ||
@@ -587,13 +587,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, | |||
587 | const u8 *addr, | 587 | const u8 *addr, |
588 | u16 mac_id, u16 color) | 588 | u16 mac_id, u16 color) |
589 | { | 589 | { |
590 | struct iwl_mvm_add_sta_cmd_v6 cmd; | 590 | struct iwl_mvm_add_sta_cmd_v7 cmd; |
591 | int ret; | 591 | int ret; |
592 | u32 status; | 592 | u32 status; |
593 | 593 | ||
594 | lockdep_assert_held(&mvm->mutex); | 594 | lockdep_assert_held(&mvm->mutex); |
595 | 595 | ||
596 | memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6)); | 596 | memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7)); |
597 | cmd.sta_id = sta->sta_id; | 597 | cmd.sta_id = sta->sta_id; |
598 | cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, | 598 | cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, |
599 | color)); | 599 | color)); |
@@ -735,7 +735,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
735 | int tid, u16 ssn, bool start) | 735 | int tid, u16 ssn, bool start) |
736 | { | 736 | { |
737 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; | 737 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; |
738 | struct iwl_mvm_add_sta_cmd_v6 cmd = {}; | 738 | struct iwl_mvm_add_sta_cmd_v7 cmd = {}; |
739 | int ret; | 739 | int ret; |
740 | u32 status; | 740 | u32 status; |
741 | 741 | ||
@@ -794,7 +794,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
794 | int tid, u8 queue, bool start) | 794 | int tid, u8 queue, bool start) |
795 | { | 795 | { |
796 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; | 796 | struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; |
797 | struct iwl_mvm_add_sta_cmd_v6 cmd = {}; | 797 | struct iwl_mvm_add_sta_cmd_v7 cmd = {}; |
798 | int ret; | 798 | int ret; |
799 | u32 status; | 799 | u32 status; |
800 | 800 | ||
@@ -833,7 +833,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
833 | return ret; | 833 | return ret; |
834 | } | 834 | } |
835 | 835 | ||
836 | static const u8 tid_to_ac[] = { | 836 | static const u8 tid_to_mac80211_ac[] = { |
837 | IEEE80211_AC_BE, | 837 | IEEE80211_AC_BE, |
838 | IEEE80211_AC_BK, | 838 | IEEE80211_AC_BK, |
839 | IEEE80211_AC_BK, | 839 | IEEE80211_AC_BK, |
@@ -844,6 +844,17 @@ static const u8 tid_to_ac[] = { | |||
844 | IEEE80211_AC_VO, | 844 | IEEE80211_AC_VO, |
845 | }; | 845 | }; |
846 | 846 | ||
847 | static const u8 tid_to_ucode_ac[] = { | ||
848 | AC_BE, | ||
849 | AC_BK, | ||
850 | AC_BK, | ||
851 | AC_BE, | ||
852 | AC_VI, | ||
853 | AC_VI, | ||
854 | AC_VO, | ||
855 | AC_VO, | ||
856 | }; | ||
857 | |||
847 | int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 858 | int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
848 | struct ieee80211_sta *sta, u16 tid, u16 *ssn) | 859 | struct ieee80211_sta *sta, u16 tid, u16 *ssn) |
849 | { | 860 | { |
@@ -874,7 +885,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | |||
874 | } | 885 | } |
875 | 886 | ||
876 | /* the new tx queue is still connected to the same mac80211 queue */ | 887 | /* the new tx queue is still connected to the same mac80211 queue */ |
877 | mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]]; | 888 | mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]]; |
878 | 889 | ||
879 | spin_lock_bh(&mvmsta->lock); | 890 | spin_lock_bh(&mvmsta->lock); |
880 | tid_data = &mvmsta->tid_data[tid]; | 891 | tid_data = &mvmsta->tid_data[tid]; |
@@ -916,7 +927,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | |||
916 | tid_data->ssn = 0xffff; | 927 | tid_data->ssn = 0xffff; |
917 | spin_unlock_bh(&mvmsta->lock); | 928 | spin_unlock_bh(&mvmsta->lock); |
918 | 929 | ||
919 | fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]]; | 930 | fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; |
920 | 931 | ||
921 | ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); | 932 | ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); |
922 | if (ret) | 933 | if (ret) |
@@ -1411,7 +1422,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, | |||
1411 | struct ieee80211_sta *sta) | 1422 | struct ieee80211_sta *sta) |
1412 | { | 1423 | { |
1413 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | 1424 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
1414 | struct iwl_mvm_add_sta_cmd_v6 cmd = { | 1425 | struct iwl_mvm_add_sta_cmd_v7 cmd = { |
1415 | .add_modify = STA_MODE_MODIFY, | 1426 | .add_modify = STA_MODE_MODIFY, |
1416 | .sta_id = mvmsta->sta_id, | 1427 | .sta_id = mvmsta->sta_id, |
1417 | .station_flags_msk = cpu_to_le32(STA_FLG_PS), | 1428 | .station_flags_msk = cpu_to_le32(STA_FLG_PS), |
@@ -1427,28 +1438,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, | |||
1427 | void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, | 1438 | void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, |
1428 | struct ieee80211_sta *sta, | 1439 | struct ieee80211_sta *sta, |
1429 | enum ieee80211_frame_release_type reason, | 1440 | enum ieee80211_frame_release_type reason, |
1430 | u16 cnt) | 1441 | u16 cnt, u16 tids, bool more_data, |
1442 | bool agg) | ||
1431 | { | 1443 | { |
1432 | u16 sleep_state_flags = | ||
1433 | (reason == IEEE80211_FRAME_RELEASE_UAPSD) ? | ||
1434 | STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; | ||
1435 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | 1444 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
1436 | struct iwl_mvm_add_sta_cmd_v6 cmd = { | 1445 | struct iwl_mvm_add_sta_cmd_v7 cmd = { |
1437 | .add_modify = STA_MODE_MODIFY, | 1446 | .add_modify = STA_MODE_MODIFY, |
1438 | .sta_id = mvmsta->sta_id, | 1447 | .sta_id = mvmsta->sta_id, |
1439 | .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, | 1448 | .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, |
1440 | .sleep_tx_count = cpu_to_le16(cnt), | 1449 | .sleep_tx_count = cpu_to_le16(cnt), |
1441 | .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), | 1450 | .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), |
1442 | /* | ||
1443 | * Same modify mask for sleep_tx_count and sleep_state_flags so | ||
1444 | * we must set the sleep_state_flags too. | ||
1445 | */ | ||
1446 | .sleep_state_flags = cpu_to_le16(sleep_state_flags), | ||
1447 | }; | 1451 | }; |
1448 | int ret; | 1452 | int tid, ret; |
1453 | unsigned long _tids = tids; | ||
1454 | |||
1455 | /* convert TIDs to ACs - we don't support TSPEC so that's OK | ||
1456 | * Note that this field is reserved and unused by firmware not | ||
1457 | * supporting GO uAPSD, so it's safe to always do this. | ||
1458 | */ | ||
1459 | for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) | ||
1460 | cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); | ||
1461 | |||
1462 | /* If we're releasing frames from aggregation queues then check if the | ||
1463 | * all queues combined that we're releasing frames from have | ||
1464 | * - more frames than the service period, in which case more_data | ||
1465 | * needs to be set | ||
1466 | * - fewer than 'cnt' frames, in which case we need to adjust the | ||
1467 | * firmware command (but do that unconditionally) | ||
1468 | */ | ||
1469 | if (agg) { | ||
1470 | int remaining = cnt; | ||
1471 | |||
1472 | spin_lock_bh(&mvmsta->lock); | ||
1473 | for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { | ||
1474 | struct iwl_mvm_tid_data *tid_data; | ||
1475 | u16 n_queued; | ||
1476 | |||
1477 | tid_data = &mvmsta->tid_data[tid]; | ||
1478 | if (WARN(tid_data->state != IWL_AGG_ON && | ||
1479 | tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, | ||
1480 | "TID %d state is %d\n", | ||
1481 | tid, tid_data->state)) { | ||
1482 | spin_unlock_bh(&mvmsta->lock); | ||
1483 | ieee80211_sta_eosp(sta); | ||
1484 | return; | ||
1485 | } | ||
1486 | |||
1487 | n_queued = iwl_mvm_tid_queued(tid_data); | ||
1488 | if (n_queued > remaining) { | ||
1489 | more_data = true; | ||
1490 | remaining = 0; | ||
1491 | break; | ||
1492 | } | ||
1493 | remaining -= n_queued; | ||
1494 | } | ||
1495 | spin_unlock_bh(&mvmsta->lock); | ||
1496 | |||
1497 | cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); | ||
1498 | if (WARN_ON(cnt - remaining == 0)) { | ||
1499 | ieee80211_sta_eosp(sta); | ||
1500 | return; | ||
1501 | } | ||
1502 | } | ||
1503 | |||
1504 | /* Note: this is ignored by firmware not supporting GO uAPSD */ | ||
1505 | if (more_data) | ||
1506 | cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); | ||
1507 | |||
1508 | if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { | ||
1509 | mvmsta->next_status_eosp = true; | ||
1510 | cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); | ||
1511 | } else { | ||
1512 | cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); | ||
1513 | } | ||
1449 | 1514 | ||
1450 | /* TODO: somehow the fw doesn't seem to take PS_POLL into account */ | ||
1451 | ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd); | 1515 | ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd); |
1452 | if (ret) | 1516 | if (ret) |
1453 | IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); | 1517 | IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); |
1454 | } | 1518 | } |
1519 | |||
1520 | int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, | ||
1521 | struct iwl_rx_cmd_buffer *rxb, | ||
1522 | struct iwl_device_cmd *cmd) | ||
1523 | { | ||
1524 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
1525 | struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; | ||
1526 | struct ieee80211_sta *sta; | ||
1527 | u32 sta_id = le32_to_cpu(notif->sta_id); | ||
1528 | |||
1529 | if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) | ||
1530 | return 0; | ||
1531 | |||
1532 | rcu_read_lock(); | ||
1533 | sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); | ||
1534 | if (!IS_ERR_OR_NULL(sta)) | ||
1535 | ieee80211_sta_eosp(sta); | ||
1536 | rcu_read_unlock(); | ||
1537 | |||
1538 | return 0; | ||
1539 | } | ||
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 4968d0237dc5..64f9a1bf7c43 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h | |||
@@ -195,24 +195,33 @@ struct iwl_mvm; | |||
195 | /** | 195 | /** |
196 | * DOC: AP mode - PS | 196 | * DOC: AP mode - PS |
197 | * | 197 | * |
198 | * When a station is asleep, the fw will set it as "asleep". All the | 198 | * When a station is asleep, the fw will set it as "asleep". All frames on |
199 | * non-aggregation frames to that station will be dropped by the fw | 199 | * shared queues (i.e. non-aggregation queues) to that station will be dropped |
200 | * (%TX_STATUS_FAIL_DEST_PS failure code). | 200 | * by the fw (%TX_STATUS_FAIL_DEST_PS failure code). |
201 | * | ||
201 | * AMPDUs are in a separate queue that is stopped by the fw. We just need to | 202 | * AMPDUs are in a separate queue that is stopped by the fw. We just need to |
202 | * let mac80211 know how many frames we have in these queues so that it can | 203 | * let mac80211 know when there are frames in these queues so that it can |
203 | * properly handle trigger frames. | 204 | * properly handle trigger frames. |
204 | * When the a trigger frame is received, mac80211 tells the driver to send | 205 | * |
205 | * frames from the AMPDU queues or AC queue depending on which queue are | 206 | * When a trigger frame is received, mac80211 tells the driver to send frames |
206 | * delivery-enabled and what TID has frames to transmit (Note that mac80211 has | 207 | * from the AMPDU queues or sends frames to non-aggregation queues itself, |
207 | * all the knowledege since all the non-agg frames are buffered / filtered, and | 208 | * depending on which ACs are delivery-enabled and what TID has frames to |
208 | * the driver tells mac80211 about agg frames). The driver needs to tell the fw | 209 | * transmit. Note that mac80211 has all the knowledege since all the non-agg |
209 | * to let frames out even if the station is asleep. This is done by | 210 | * frames are buffered / filtered, and the driver tells mac80211 about agg |
210 | * %iwl_mvm_sta_modify_sleep_tx_count. | 211 | * frames). The driver needs to tell the fw to let frames out even if the |
211 | * When we receive a frame from that station with PM bit unset, the | 212 | * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. |
212 | * driver needs to let the fw know that this station isn't alseep any more. | 213 | * |
213 | * This is done by %iwl_mvm_sta_modify_ps_wake. | 214 | * When we receive a frame from that station with PM bit unset, the driver |
214 | * | 215 | * needs to let the fw know that this station isn't asleep any more. This is |
215 | * TODO - EOSP handling | 216 | * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the |
217 | * station's wakeup. | ||
218 | * | ||
219 | * For a GO, the Service Period might be cut short due to an absence period | ||
220 | * of the GO. In this (and all other cases) the firmware notifies us with the | ||
221 | * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we | ||
222 | * already sent to the device will be rejected again. | ||
223 | * | ||
224 | * See also "AP support for powersaving clients" in mac80211.h. | ||
216 | */ | 225 | */ |
217 | 226 | ||
218 | /** | 227 | /** |
@@ -261,6 +270,12 @@ struct iwl_mvm_tid_data { | |||
261 | u16 ssn; | 270 | u16 ssn; |
262 | }; | 271 | }; |
263 | 272 | ||
273 | static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) | ||
274 | { | ||
275 | return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), | ||
276 | tid_data->next_reclaimed); | ||
277 | } | ||
278 | |||
264 | /** | 279 | /** |
265 | * struct iwl_mvm_sta - representation of a station in the driver | 280 | * struct iwl_mvm_sta - representation of a station in the driver |
266 | * @sta_id: the index of the station in the fw (will be replaced by id_n_color) | 281 | * @sta_id: the index of the station in the fw (will be replaced by id_n_color) |
@@ -270,6 +285,8 @@ struct iwl_mvm_tid_data { | |||
270 | * tid. | 285 | * tid. |
271 | * @max_agg_bufsize: the maximal size of the AGG buffer for this station | 286 | * @max_agg_bufsize: the maximal size of the AGG buffer for this station |
272 | * @bt_reduced_txpower: is reduced tx power enabled for this station | 287 | * @bt_reduced_txpower: is reduced tx power enabled for this station |
288 | * @next_status_eosp: the next reclaimed packet is a PS-Poll response and | ||
289 | * we need to signal the EOSP | ||
273 | * @lock: lock to protect the whole struct. Since %tid_data is access from Tx | 290 | * @lock: lock to protect the whole struct. Since %tid_data is access from Tx |
274 | * and from Tx response flow, it needs a spinlock. | 291 | * and from Tx response flow, it needs a spinlock. |
275 | * @tid_data: per tid data. Look at %iwl_mvm_tid_data. | 292 | * @tid_data: per tid data. Look at %iwl_mvm_tid_data. |
@@ -288,6 +305,7 @@ struct iwl_mvm_sta { | |||
288 | u16 tid_disable_agg; | 305 | u16 tid_disable_agg; |
289 | u8 max_agg_bufsize; | 306 | u8 max_agg_bufsize; |
290 | bool bt_reduced_txpower; | 307 | bool bt_reduced_txpower; |
308 | bool next_status_eosp; | ||
291 | spinlock_t lock; | 309 | spinlock_t lock; |
292 | struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; | 310 | struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; |
293 | struct iwl_lq_sta lq_sta; | 311 | struct iwl_lq_sta lq_sta; |
@@ -345,6 +363,10 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, | |||
345 | struct ieee80211_sta *sta, u32 iv32, | 363 | struct ieee80211_sta *sta, u32 iv32, |
346 | u16 *phase1key); | 364 | u16 *phase1key); |
347 | 365 | ||
366 | int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, | ||
367 | struct iwl_rx_cmd_buffer *rxb, | ||
368 | struct iwl_device_cmd *cmd); | ||
369 | |||
348 | /* AMPDU */ | 370 | /* AMPDU */ |
349 | int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | 371 | int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, |
350 | int tid, u16 ssn, bool start); | 372 | int tid, u16 ssn, bool start); |
@@ -375,7 +397,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, | |||
375 | void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, | 397 | void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, |
376 | struct ieee80211_sta *sta, | 398 | struct ieee80211_sta *sta, |
377 | enum ieee80211_frame_release_type reason, | 399 | enum ieee80211_frame_release_type reason, |
378 | u16 cnt); | 400 | u16 cnt, u16 tids, bool more_data, |
401 | bool agg); | ||
379 | int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, | 402 | int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, |
380 | bool drain); | 403 | bool drain); |
381 | 404 | ||
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 90378c217bc7..8d18bf23e4bf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c | |||
@@ -377,6 +377,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, | |||
377 | tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; | 377 | tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; |
378 | /* From now on, we cannot access info->control */ | 378 | /* From now on, we cannot access info->control */ |
379 | 379 | ||
380 | /* | ||
381 | * we handle that entirely ourselves -- for uAPSD the firmware | ||
382 | * will always send a notification, and for PS-Poll responses | ||
383 | * we'll notify mac80211 when getting frame status | ||
384 | */ | ||
385 | info->flags &= ~IEEE80211_TX_STATUS_EOSP; | ||
386 | |||
380 | spin_lock(&mvmsta->lock); | 387 | spin_lock(&mvmsta->lock); |
381 | 388 | ||
382 | if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { | 389 | if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { |
@@ -437,6 +444,17 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, | |||
437 | 444 | ||
438 | lockdep_assert_held(&mvmsta->lock); | 445 | lockdep_assert_held(&mvmsta->lock); |
439 | 446 | ||
447 | if ((tid_data->state == IWL_AGG_ON || | ||
448 | tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && | ||
449 | iwl_mvm_tid_queued(tid_data) == 0) { | ||
450 | /* | ||
451 | * Now that this aggregation queue is empty tell mac80211 so it | ||
452 | * knows we no longer have frames buffered for the station on | ||
453 | * this TID (for the TIM bitmap calculation.) | ||
454 | */ | ||
455 | ieee80211_sta_set_buffered(sta, tid, false); | ||
456 | } | ||
457 | |||
440 | if (tid_data->ssn != tid_data->next_reclaimed) | 458 | if (tid_data->ssn != tid_data->next_reclaimed) |
441 | return; | 459 | return; |
442 | 460 | ||
@@ -674,6 +692,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, | |||
674 | iwl_mvm_check_ratid_empty(mvm, sta, tid); | 692 | iwl_mvm_check_ratid_empty(mvm, sta, tid); |
675 | spin_unlock_bh(&mvmsta->lock); | 693 | spin_unlock_bh(&mvmsta->lock); |
676 | } | 694 | } |
695 | |||
696 | if (mvmsta->next_status_eosp) { | ||
697 | mvmsta->next_status_eosp = false; | ||
698 | ieee80211_sta_eosp(sta); | ||
699 | } | ||
677 | } else { | 700 | } else { |
678 | sta = NULL; | 701 | sta = NULL; |
679 | mvmsta = NULL; | 702 | mvmsta = NULL; |