aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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
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')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c42
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c17
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c29
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c51
8 files changed, 113 insertions, 42 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 1c6506529b6e..6385cdf24d13 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2744,6 +2744,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw,
2744 return 0; 2744 return 0;
2745} 2745}
2746 2746
2747static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
2748 struct ieee80211_vif *vif,
2749 enum sta_notify_cmd cmd,
2750 struct ieee80211_sta *sta)
2751{
2752 struct iwl_priv *priv = hw->priv;
2753 struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
2754 int sta_id;
2755
2756 /*
2757 * TODO: We really should use this callback to
2758 * actually maintain the station table in
2759 * the device.
2760 */
2761
2762 switch (cmd) {
2763 case STA_NOTIFY_ADD:
2764 atomic_set(&sta_priv->pending_frames, 0);
2765 if (vif->type == NL80211_IFTYPE_AP)
2766 sta_priv->client = true;
2767 break;
2768 case STA_NOTIFY_SLEEP:
2769 WARN_ON(!sta_priv->client);
2770 sta_priv->asleep = true;
2771 if (atomic_read(&sta_priv->pending_frames) > 0)
2772 ieee80211_sta_block_awake(hw, sta, true);
2773 break;
2774 case STA_NOTIFY_AWAKE:
2775 WARN_ON(!sta_priv->client);
2776 sta_priv->asleep = false;
2777 sta_id = iwl_find_station(priv, sta->addr);
2778 if (sta_id != IWL_INVALID_STATION)
2779 iwl_sta_modify_ps_wake(priv, sta_id);
2780 break;
2781 default:
2782 break;
2783 }
2784}
2785
2747/***************************************************************************** 2786/*****************************************************************************
2748 * 2787 *
2749 * sysfs attributes 2788 * sysfs attributes
@@ -3175,7 +3214,8 @@ static struct ieee80211_ops iwl_hw_ops = {
3175 .reset_tsf = iwl_mac_reset_tsf, 3214 .reset_tsf = iwl_mac_reset_tsf,
3176 .bss_info_changed = iwl_bss_info_changed, 3215 .bss_info_changed = iwl_bss_info_changed,
3177 .ampdu_action = iwl_mac_ampdu_action, 3216 .ampdu_action = iwl_mac_ampdu_action,
3178 .hw_scan = iwl_mac_hw_scan 3217 .hw_scan = iwl_mac_hw_scan,
3218 .sta_notify = iwl_mac_sta_notify,
3179}; 3219};
3180 3220
3181static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 3221static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index aa4e38cbf071..e91507531923 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -977,6 +977,7 @@ struct iwl_qosparam_cmd {
977#define STA_MODIFY_TX_RATE_MSK 0x04 977#define STA_MODIFY_TX_RATE_MSK 0x04
978#define STA_MODIFY_ADDBA_TID_MSK 0x08 978#define STA_MODIFY_ADDBA_TID_MSK 0x08
979#define STA_MODIFY_DELBA_TID_MSK 0x10 979#define STA_MODIFY_DELBA_TID_MSK 0x10
980#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
980 981
981/* Receiver address (actually, Rx station's index into station table), 982/* Receiver address (actually, Rx station's index into station table),
982 * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ 983 * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index ba9c96ff27ca..9a5ca25d72c3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -336,8 +336,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
336 pos += scnprintf(buf + pos, bufsz - pos, 336 pos += scnprintf(buf + pos, bufsz - pos,
337 "flags: 0x%x\n", 337 "flags: 0x%x\n",
338 station->sta.station_flags_msk); 338 station->sta.station_flags_msk);
339 pos += scnprintf(buf + pos, bufsz - pos,
340 "ps_status: %u\n", station->ps_status);
341 pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n"); 339 pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n");
342 pos += scnprintf(buf + pos, bufsz - pos, 340 pos += scnprintf(buf + pos, bufsz - pos,
343 "seq_num\t\ttxq_id"); 341 "seq_num\t\ttxq_id");
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index a474383ec0b8..f1601cfebc29 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -545,15 +545,11 @@ struct iwl_qos_info {
545 struct iwl_qosparam_cmd def_qos_parm; 545 struct iwl_qosparam_cmd def_qos_parm;
546}; 546};
547 547
548#define STA_PS_STATUS_WAKE 0
549#define STA_PS_STATUS_SLEEP 1
550
551 548
552struct iwl3945_station_entry { 549struct iwl3945_station_entry {
553 struct iwl3945_addsta_cmd sta; 550 struct iwl3945_addsta_cmd sta;
554 struct iwl_tid_data tid[MAX_TID_COUNT]; 551 struct iwl_tid_data tid[MAX_TID_COUNT];
555 u8 used; 552 u8 used;
556 u8 ps_status;
557 struct iwl_hw_key keyinfo; 553 struct iwl_hw_key keyinfo;
558}; 554};
559 555
@@ -561,7 +557,6 @@ struct iwl_station_entry {
561 struct iwl_addsta_cmd sta; 557 struct iwl_addsta_cmd sta;
562 struct iwl_tid_data tid[MAX_TID_COUNT]; 558 struct iwl_tid_data tid[MAX_TID_COUNT];
563 u8 used; 559 u8 used;
564 u8 ps_status;
565 struct iwl_hw_key keyinfo; 560 struct iwl_hw_key keyinfo;
566}; 561};
567 562
@@ -571,11 +566,12 @@ struct iwl_station_entry {
571 * When mac80211 creates a station it reserves some space (hw->sta_data_size) 566 * When mac80211 creates a station it reserves some space (hw->sta_data_size)
572 * in the structure for use by driver. This structure is places in that 567 * in the structure for use by driver. This structure is places in that
573 * space. 568 * space.
574 *
575 * At the moment use it for the station's rate scaling information.
576 */ 569 */
577struct iwl_station_priv { 570struct iwl_station_priv {
578 struct iwl_lq_sta lq_sta; 571 struct iwl_lq_sta lq_sta;
572 atomic_t pending_frames;
573 bool client;
574 bool asleep;
579}; 575};
580 576
581/* one for each uCode image (inst/data, boot/init/runtime) */ 577/* one for each uCode image (inst/data, boot/init/runtime) */
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 9d010a0d83af..cc980d5d57c2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -1028,7 +1028,6 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
1028 struct iwl4965_rx_mpdu_res_start *amsdu; 1028 struct iwl4965_rx_mpdu_res_start *amsdu;
1029 u32 len; 1029 u32 len;
1030 u32 ampdu_status; 1030 u32 ampdu_status;
1031 u16 fc;
1032 u32 rate_n_flags; 1031 u32 rate_n_flags;
1033 1032
1034 /** 1033 /**
@@ -1161,20 +1160,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
1161 priv->last_tsf = le64_to_cpu(phy_res->timestamp); 1160 priv->last_tsf = le64_to_cpu(phy_res->timestamp);
1162 } 1161 }
1163 1162
1164 fc = le16_to_cpu(header->frame_control); 1163 iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
1165 switch (fc & IEEE80211_FCTL_FTYPE) { 1164 rxb, &rx_status);
1166 case IEEE80211_FTYPE_MGMT:
1167 case IEEE80211_FTYPE_DATA:
1168 if (priv->iw_mode == NL80211_IFTYPE_AP)
1169 iwl_update_ps_mode(priv, fc & IEEE80211_FCTL_PM,
1170 header->addr2);
1171 /* fall through */
1172 default:
1173 iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
1174 rxb, &rx_status);
1175 break;
1176
1177 }
1178} 1165}
1179EXPORT_SYMBOL(iwl_rx_reply_rx); 1166EXPORT_SYMBOL(iwl_rx_reply_rx);
1180 1167
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
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h
index 1c382de80d49..8d052de2d405 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.h
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.h
@@ -66,5 +66,6 @@ void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
66int iwl_sta_rx_agg_start(struct iwl_priv *priv, 66int iwl_sta_rx_agg_start(struct iwl_priv *priv,
67 const u8 *addr, int tid, u16 ssn); 67 const u8 *addr, int tid, u16 ssn);
68int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid); 68int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
69void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr); 69void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id);
70void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt);
70#endif /* __iwl_sta_h__ */ 71#endif /* __iwl_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 9370e062000d..ebfc460115d6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -709,6 +709,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
709{ 709{
710 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 710 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
711 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 711 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
712 struct ieee80211_sta *sta = info->control.sta;
713 struct iwl_station_priv *sta_priv = NULL;
712 struct iwl_tx_queue *txq; 714 struct iwl_tx_queue *txq;
713 struct iwl_queue *q; 715 struct iwl_queue *q;
714 struct iwl_device_cmd *out_cmd; 716 struct iwl_device_cmd *out_cmd;
@@ -771,6 +773,24 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
771 773
772 IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); 774 IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
773 775
776 if (sta)
777 sta_priv = (void *)sta->drv_priv;
778
779 if (sta_priv && sta_id != priv->hw_params.bcast_sta_id &&
780 sta_priv->asleep) {
781 WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE));
782 /*
783 * This sends an asynchronous command to the device,
784 * but we can rely on it being processed before the
785 * next frame is processed -- and the next frame to
786 * this station is the one that will consume this
787 * counter.
788 * For now set the counter to just 1 since we do not
789 * support uAPSD yet.
790 */
791 iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
792 }
793
774 txq_id = skb_get_queue_mapping(skb); 794 txq_id = skb_get_queue_mapping(skb);
775 if (ieee80211_is_data_qos(fc)) { 795 if (ieee80211_is_data_qos(fc)) {
776 qc = ieee80211_get_qos_ctl(hdr); 796 qc = ieee80211_get_qos_ctl(hdr);
@@ -930,6 +950,17 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
930 ret = iwl_txq_update_write_ptr(priv, txq); 950 ret = iwl_txq_update_write_ptr(priv, txq);
931 spin_unlock_irqrestore(&priv->lock, flags); 951 spin_unlock_irqrestore(&priv->lock, flags);
932 952
953 /*
954 * At this point the frame is "transmitted" successfully
955 * and we will get a TX status notification eventually,
956 * regardless of the value of ret. "ret" only indicates
957 * whether or not we should update the write pointer.
958 */
959
960 /* avoid atomic ops if it isn't an associated client */
961 if (sta_priv && sta_priv->client)
962 atomic_inc(&sta_priv->pending_frames);
963
933 if (ret) 964 if (ret)
934 return ret; 965 return ret;
935 966
@@ -1074,6 +1105,24 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
1074 return ret ? ret : idx; 1105 return ret ? ret : idx;
1075} 1106}
1076 1107
1108static void iwl_tx_status(struct iwl_priv *priv, struct sk_buff *skb)
1109{
1110 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1111 struct ieee80211_sta *sta;
1112 struct iwl_station_priv *sta_priv;
1113
1114 sta = ieee80211_find_sta(priv->vif, hdr->addr1);
1115 if (sta) {
1116 sta_priv = (void *)sta->drv_priv;
1117 /* avoid atomic ops if this isn't a client */
1118 if (sta_priv->client &&
1119 atomic_dec_return(&sta_priv->pending_frames) == 0)
1120 ieee80211_sta_block_awake(priv->hw, sta, false);
1121 }
1122
1123 ieee80211_tx_status_irqsafe(priv->hw, skb);
1124}
1125
1077int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) 1126int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
1078{ 1127{
1079 struct iwl_tx_queue *txq = &priv->txq[txq_id]; 1128 struct iwl_tx_queue *txq = &priv->txq[txq_id];
@@ -1093,7 +1142,7 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
1093 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { 1142 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
1094 1143
1095 tx_info = &txq->txb[txq->q.read_ptr]; 1144 tx_info = &txq->txb[txq->q.read_ptr];
1096 ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]); 1145 iwl_tx_status(priv, tx_info->skb[0]);
1097 tx_info->skb[0] = NULL; 1146 tx_info->skb[0] = NULL;
1098 1147
1099 if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) 1148 if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)