aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)