aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-sta.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-11-13 14:56:37 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-11-18 17:09:08 -0500
commit6ab10ff8738dfb098fd32132b7ebcf5cdb43ebde (patch)
tree7bb2cf0ce4d1b286aa14f4e0ae9d24dfee8b75c2 /drivers/net/wireless/iwlwifi/iwl-sta.c
parent9bb487b406692e172b15eba58de7d69358ac2005 (diff)
iwlwifi: handle unicast PS buffering
Using the new mac80211 functionality, this makes iwlwifi handle unicast PS buffering correctly. The device works like this: * when a station goes to sleep, the microcode notices this and marks the station as asleep * when the station is marked asleep, the microcode refuses to transmit to the station and rejects all frames queued to it with the failure status code TX_STATUS_FAIL_DEST_PS (a previous patch handled this correctly) * when we need to send frames to the station _although_ it is asleep, we need to tell the ucode how many, and this is asynchronous with sending so we cannot just send the frames, we need to wait for all other frames to be flushed, and then update the counter before sending out the poll response frames. This is handled partially in the driver and partially in mac80211. In order to do all this correctly, we need to * keep track of how many frames are pending for each associated client station (avoid doing it for other stations to avoid the atomic ops) * tell mac80211 that we driver-block the PS status while there are still frames pending on the queues, and once they are all rejected (due to the dest sta being in PS) unblock mac80211 Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-sta.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c29
1 files changed, 14 insertions, 15 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index eba36f737388..cd6a6901216e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -1216,7 +1216,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
1216} 1216}
1217EXPORT_SYMBOL(iwl_sta_rx_agg_stop); 1217EXPORT_SYMBOL(iwl_sta_rx_agg_stop);
1218 1218
1219static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) 1219void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
1220{ 1220{
1221 unsigned long flags; 1221 unsigned long flags;
1222 1222
@@ -1224,27 +1224,26 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
1224 priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; 1224 priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
1225 priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; 1225 priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
1226 priv->stations[sta_id].sta.sta.modify_mask = 0; 1226 priv->stations[sta_id].sta.sta.modify_mask = 0;
1227 priv->stations[sta_id].sta.sleep_tx_count = 0;
1227 priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 1228 priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
1228 spin_unlock_irqrestore(&priv->sta_lock, flags); 1229 spin_unlock_irqrestore(&priv->sta_lock, flags);
1229 1230
1230 iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); 1231 iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
1231} 1232}
1233EXPORT_SYMBOL(iwl_sta_modify_ps_wake);
1232 1234
1233void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr) 1235void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
1234{ 1236{
1235 /* FIXME: need locking over ps_status ??? */ 1237 unsigned long flags;
1236 u8 sta_id = iwl_find_station(priv, addr);
1237 1238
1238 if (sta_id != IWL_INVALID_STATION) { 1239 spin_lock_irqsave(&priv->sta_lock, flags);
1239 u8 sta_awake = priv->stations[sta_id]. 1240 priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
1240 ps_status == STA_PS_STATUS_WAKE; 1241 priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
1242 priv->stations[sta_id].sta.sta.modify_mask =
1243 STA_MODIFY_SLEEP_TX_COUNT_MSK;
1244 priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
1245 priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
1246 spin_unlock_irqrestore(&priv->sta_lock, flags);
1241 1247
1242 if (sta_awake && ps_bit) 1248 iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
1243 priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
1244 else if (!sta_awake && !ps_bit) {
1245 iwl_sta_modify_ps_wake(priv, sta_id);
1246 priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
1247 }
1248 }
1249} 1249}
1250