diff options
author | Ron Rindjunsky <ron.rindjunsky@intel.com> | 2007-12-25 10:00:33 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:00:59 -0500 |
commit | 07db218396650933abff3c5c1ad1e2a6e0cfedeb (patch) | |
tree | e81672015dba20e146b2eea30aea9449f5833a44 | |
parent | 5aae2880618471cfa679ca22531b88990bee9bf8 (diff) |
mac80211: A-MPDU Rx adding basic functionality
This patch adds the basic needed abilities and functions for A-MPDU Rx session
changed functions:
- ieee80211_sta_process_addba_request - Rx A-MPDU initialization enabled
- ieee80211_stop - stops all A-MPDU Rx in case interface goes down
added functions:
- ieee80211_send_delba - used for sending out Del BA in A-MPDU sessions
- ieee80211_sta_stop_rx_BA_session - stopping Rx A-MPDU session
- sta_rx_agg_session_timer_expired - stops A-MPDU Rx use if load is too
low
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/ieee80211.h | 7 | ||||
-rw-r--r-- | net/mac80211/ieee80211.c | 9 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 221 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 3 |
5 files changed, 232 insertions, 11 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 3e641590d0c8..4d5a4c9dcba7 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -472,6 +472,13 @@ enum ieee80211_back_actioncode { | |||
472 | WLAN_ACTION_DELBA = 2, | 472 | WLAN_ACTION_DELBA = 2, |
473 | }; | 473 | }; |
474 | 474 | ||
475 | /* BACK (block-ack) parties */ | ||
476 | enum ieee80211_back_parties { | ||
477 | WLAN_BACK_RECIPIENT = 0, | ||
478 | WLAN_BACK_INITIATOR = 1, | ||
479 | WLAN_BACK_TIMER = 2, | ||
480 | }; | ||
481 | |||
475 | /* A-MSDU 802.11n */ | 482 | /* A-MSDU 802.11n */ |
476 | #define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 | 483 | #define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 |
477 | 484 | ||
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 9c14e3d303c5..2011c726f2b1 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c | |||
@@ -292,9 +292,18 @@ static int ieee80211_stop(struct net_device *dev) | |||
292 | struct ieee80211_sub_if_data *sdata; | 292 | struct ieee80211_sub_if_data *sdata; |
293 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 293 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
294 | struct ieee80211_if_init_conf conf; | 294 | struct ieee80211_if_init_conf conf; |
295 | struct sta_info *sta; | ||
296 | int i; | ||
295 | 297 | ||
296 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 298 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
297 | 299 | ||
300 | list_for_each_entry(sta, &local->sta_list, list) { | ||
301 | for (i = 0; i < STA_TID_NUM; i++) | ||
302 | ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, | ||
303 | i, WLAN_BACK_RECIPIENT, | ||
304 | WLAN_REASON_QSTA_LEAVE_QBSS); | ||
305 | } | ||
306 | |||
298 | netif_stop_queue(dev); | 307 | netif_stop_queue(dev); |
299 | 308 | ||
300 | /* | 309 | /* |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index baf53c047127..740d69d5bbd3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -767,6 +767,9 @@ int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, | |||
767 | int ieee80211_ht_addt_info_ie_to_ht_bss_info( | 767 | int ieee80211_ht_addt_info_ie_to_ht_bss_info( |
768 | struct ieee80211_ht_addt_info *ht_add_info_ie, | 768 | struct ieee80211_ht_addt_info *ht_add_info_ie, |
769 | struct ieee80211_ht_bss_info *bss_info); | 769 | struct ieee80211_ht_bss_info *bss_info); |
770 | void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da, | ||
771 | u16 tid, u16 initiator, u16 reason); | ||
772 | void sta_rx_agg_session_timer_expired(unsigned long data); | ||
770 | /* ieee80211_iface.c */ | 773 | /* ieee80211_iface.c */ |
771 | int ieee80211_if_add(struct net_device *dev, const char *name, | 774 | int ieee80211_if_add(struct net_device *dev, const char *name, |
772 | struct net_device **new_dev, int type); | 775 | struct net_device **new_dev, int type); |
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index 5b8f484c1673..d5a7683fab3a 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c | |||
@@ -64,6 +64,11 @@ | |||
64 | #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C | 64 | #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C |
65 | #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 | 65 | #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 |
66 | 66 | ||
67 | /* next values represent the buffer size for A-MPDU frame. | ||
68 | * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */ | ||
69 | #define IEEE80211_MIN_AMPDU_BUF 0x8 | ||
70 | #define IEEE80211_MAX_AMPDU_BUF 0x40 | ||
71 | |||
67 | static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, | 72 | static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, |
68 | u8 *ssid, size_t ssid_len); | 73 | u8 *ssid, size_t ssid_len); |
69 | static struct ieee80211_sta_bss * | 74 | static struct ieee80211_sta_bss * |
@@ -1005,7 +1010,8 @@ static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid, | |||
1005 | struct ieee80211_mgmt *mgmt; | 1010 | struct ieee80211_mgmt *mgmt; |
1006 | u16 capab; | 1011 | u16 capab; |
1007 | 1012 | ||
1008 | skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); | 1013 | skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 + |
1014 | sizeof(mgmt->u.action.u.addba_resp)); | ||
1009 | if (!skb) { | 1015 | if (!skb) { |
1010 | printk(KERN_DEBUG "%s: failed to allocate buffer " | 1016 | printk(KERN_DEBUG "%s: failed to allocate buffer " |
1011 | "for addba resp frame\n", dev->name); | 1017 | "for addba resp frame\n", dev->name); |
@@ -1047,9 +1053,14 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev, | |||
1047 | size_t len) | 1053 | size_t len) |
1048 | { | 1054 | { |
1049 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 1055 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
1056 | struct ieee80211_hw *hw = &local->hw; | ||
1057 | struct ieee80211_conf *conf = &hw->conf; | ||
1050 | struct sta_info *sta; | 1058 | struct sta_info *sta; |
1051 | u16 capab, tid, timeout, ba_policy, buf_size, status; | 1059 | struct tid_ampdu_rx *tid_agg_rx; |
1060 | u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; | ||
1052 | u8 dialog_token; | 1061 | u8 dialog_token; |
1062 | int ret = -EOPNOTSUPP; | ||
1063 | DECLARE_MAC_BUF(mac); | ||
1053 | 1064 | ||
1054 | sta = sta_info_get(local, mgmt->sa); | 1065 | sta = sta_info_get(local, mgmt->sa); |
1055 | if (!sta) | 1066 | if (!sta) |
@@ -1058,28 +1069,216 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev, | |||
1058 | /* extract session parameters from addba request frame */ | 1069 | /* extract session parameters from addba request frame */ |
1059 | dialog_token = mgmt->u.action.u.addba_req.dialog_token; | 1070 | dialog_token = mgmt->u.action.u.addba_req.dialog_token; |
1060 | timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); | 1071 | timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); |
1072 | start_seq_num = | ||
1073 | le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; | ||
1061 | 1074 | ||
1062 | capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); | 1075 | capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); |
1063 | ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; | 1076 | ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; |
1064 | tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; | 1077 | tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; |
1065 | buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; | 1078 | buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; |
1066 | 1079 | ||
1067 | /* TODO - currently aggregation is declined (A-MPDU add BA request | ||
1068 | * acceptance is not obligatory by 802.11n draft), but here is | ||
1069 | * the entry point for dealing with it */ | ||
1070 | #ifdef MAC80211_HT_DEBUG | ||
1071 | if (net_ratelimit()) | ||
1072 | printk(KERN_DEBUG "Add Block Ack request arrived," | ||
1073 | " currently denying it\n"); | ||
1074 | #endif /* MAC80211_HT_DEBUG */ | ||
1075 | |||
1076 | status = WLAN_STATUS_REQUEST_DECLINED; | 1080 | status = WLAN_STATUS_REQUEST_DECLINED; |
1077 | 1081 | ||
1082 | /* sanity check for incoming parameters: | ||
1083 | * check if configuration can support the BA policy | ||
1084 | * and if buffer size does not exceeds max value */ | ||
1085 | if (((ba_policy != 1) | ||
1086 | && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA))) | ||
1087 | || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { | ||
1088 | status = WLAN_STATUS_INVALID_QOS_PARAM; | ||
1089 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
1090 | if (net_ratelimit()) | ||
1091 | printk(KERN_DEBUG "Block Ack Req with bad params from " | ||
1092 | "%s on tid %u. policy %d, buffer size %d\n", | ||
1093 | print_mac(mac, mgmt->sa), tid, ba_policy, | ||
1094 | buf_size); | ||
1095 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
1096 | goto end_no_lock; | ||
1097 | } | ||
1098 | /* determine default buffer size */ | ||
1099 | if (buf_size == 0) { | ||
1100 | struct ieee80211_hw_mode *mode = conf->mode; | ||
1101 | buf_size = IEEE80211_MIN_AMPDU_BUF; | ||
1102 | buf_size = buf_size << mode->ht_info.ampdu_factor; | ||
1103 | } | ||
1104 | |||
1105 | tid_agg_rx = &sta->ampdu_mlme.tid_rx[tid]; | ||
1106 | |||
1107 | /* examine state machine */ | ||
1108 | spin_lock_bh(&sta->ampdu_mlme.ampdu_rx); | ||
1109 | |||
1110 | if (tid_agg_rx->state != HT_AGG_STATE_IDLE) { | ||
1111 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
1112 | if (net_ratelimit()) | ||
1113 | printk(KERN_DEBUG "unexpected Block Ack Req from " | ||
1114 | "%s on tid %u\n", | ||
1115 | print_mac(mac, mgmt->sa), tid); | ||
1116 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
1117 | goto end; | ||
1118 | } | ||
1119 | |||
1120 | /* prepare reordering buffer */ | ||
1121 | tid_agg_rx->reorder_buf = | ||
1122 | kmalloc(buf_size * sizeof(struct sk_buf *), GFP_ATOMIC); | ||
1123 | if ((!tid_agg_rx->reorder_buf) && net_ratelimit()) { | ||
1124 | printk(KERN_ERR "can not allocate reordering buffer " | ||
1125 | "to tid %d\n", tid); | ||
1126 | goto end; | ||
1127 | } | ||
1128 | memset(tid_agg_rx->reorder_buf, 0, | ||
1129 | buf_size * sizeof(struct sk_buf *)); | ||
1130 | |||
1131 | if (local->ops->ampdu_action) | ||
1132 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START, | ||
1133 | sta->addr, tid, start_seq_num); | ||
1134 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
1135 | printk(KERN_DEBUG "Rx A-MPDU on tid %d result %d", tid, ret); | ||
1136 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
1137 | |||
1138 | if (ret) { | ||
1139 | kfree(tid_agg_rx->reorder_buf); | ||
1140 | goto end; | ||
1141 | } | ||
1142 | |||
1143 | /* change state and send addba resp */ | ||
1144 | tid_agg_rx->state = HT_AGG_STATE_OPERATIONAL; | ||
1145 | tid_agg_rx->dialog_token = dialog_token; | ||
1146 | tid_agg_rx->ssn = start_seq_num; | ||
1147 | tid_agg_rx->head_seq_num = start_seq_num; | ||
1148 | tid_agg_rx->buf_size = buf_size; | ||
1149 | tid_agg_rx->timeout = timeout; | ||
1150 | tid_agg_rx->stored_mpdu_num = 0; | ||
1151 | status = WLAN_STATUS_SUCCESS; | ||
1152 | end: | ||
1153 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); | ||
1154 | |||
1155 | end_no_lock: | ||
1078 | ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token, | 1156 | ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token, |
1079 | status, 1, buf_size, timeout); | 1157 | status, 1, buf_size, timeout); |
1080 | sta_info_put(sta); | 1158 | sta_info_put(sta); |
1081 | } | 1159 | } |
1082 | 1160 | ||
1161 | void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, | ||
1162 | u16 initiator, u16 reason_code) | ||
1163 | { | ||
1164 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
1165 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
1166 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | ||
1167 | struct sk_buff *skb; | ||
1168 | struct ieee80211_mgmt *mgmt; | ||
1169 | u16 params; | ||
1170 | |||
1171 | skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 + | ||
1172 | sizeof(mgmt->u.action.u.delba)); | ||
1173 | |||
1174 | if (!skb) { | ||
1175 | printk(KERN_ERR "%s: failed to allocate buffer " | ||
1176 | "for delba frame\n", dev->name); | ||
1177 | return; | ||
1178 | } | ||
1179 | |||
1180 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
1181 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | ||
1182 | memset(mgmt, 0, 24); | ||
1183 | memcpy(mgmt->da, da, ETH_ALEN); | ||
1184 | memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); | ||
1185 | if (sdata->type == IEEE80211_IF_TYPE_AP) | ||
1186 | memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN); | ||
1187 | else | ||
1188 | memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); | ||
1189 | mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, | ||
1190 | IEEE80211_STYPE_ACTION); | ||
1191 | |||
1192 | skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); | ||
1193 | |||
1194 | mgmt->u.action.category = WLAN_CATEGORY_BACK; | ||
1195 | mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; | ||
1196 | params = (u16)(initiator << 11); /* bit 11 initiator */ | ||
1197 | params |= (u16)(tid << 12); /* bit 15:12 TID number */ | ||
1198 | |||
1199 | mgmt->u.action.u.delba.params = cpu_to_le16(params); | ||
1200 | mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); | ||
1201 | |||
1202 | ieee80211_sta_tx(dev, skb, 0); | ||
1203 | } | ||
1204 | |||
1205 | void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, | ||
1206 | u16 initiator, u16 reason) | ||
1207 | { | ||
1208 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
1209 | struct ieee80211_hw *hw = &local->hw; | ||
1210 | struct sta_info *sta; | ||
1211 | int ret; | ||
1212 | |||
1213 | sta = sta_info_get(local, ra); | ||
1214 | if (!sta) | ||
1215 | return; | ||
1216 | |||
1217 | /* check if TID is in operational state */ | ||
1218 | spin_lock_bh(&sta->ampdu_mlme.ampdu_rx); | ||
1219 | if (sta->ampdu_mlme.tid_rx[tid].state | ||
1220 | != HT_AGG_STATE_OPERATIONAL) { | ||
1221 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); | ||
1222 | if (net_ratelimit()) | ||
1223 | printk(KERN_DEBUG "rx BA session requested to stop on " | ||
1224 | "inactive tid %d\n", tid); | ||
1225 | sta_info_put(sta); | ||
1226 | return; | ||
1227 | } | ||
1228 | sta->ampdu_mlme.tid_rx[tid].state = | ||
1229 | HT_AGG_STATE_REQ_STOP_BA_MSK | | ||
1230 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); | ||
1231 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); | ||
1232 | |||
1233 | /* stop HW Rx aggregation. ampdu_action existence | ||
1234 | * already verified in session init so we add the BUG_ON */ | ||
1235 | BUG_ON(!local->ops->ampdu_action); | ||
1236 | |||
1237 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP, | ||
1238 | ra, tid, EINVAL); | ||
1239 | if (ret) | ||
1240 | printk(KERN_DEBUG "HW problem - can not stop rx " | ||
1241 | "aggergation for tid %d\n", tid); | ||
1242 | |||
1243 | /* shutdown timer has not expired */ | ||
1244 | if (initiator != WLAN_BACK_TIMER) | ||
1245 | del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]. | ||
1246 | session_timer); | ||
1247 | |||
1248 | /* check if this is a self generated aggregation halt */ | ||
1249 | if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) | ||
1250 | ieee80211_send_delba(dev, ra, tid, 0, reason); | ||
1251 | |||
1252 | /* free the reordering buffer */ | ||
1253 | kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf); | ||
1254 | |||
1255 | sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE; | ||
1256 | sta_info_put(sta); | ||
1257 | } | ||
1258 | |||
1259 | /* | ||
1260 | * After receiving Block Ack Request (BAR) we activated a | ||
1261 | * timer after each frame arrives from the originator. | ||
1262 | * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed. | ||
1263 | */ | ||
1264 | void sta_rx_agg_session_timer_expired(unsigned long data) | ||
1265 | { | ||
1266 | /* not an elegant detour, but there is no choice as the timer passes | ||
1267 | * only one argument, and verious sta_info are needed here, so init | ||
1268 | * flow in sta_info_add gives the TID as data, while the timer_to_id | ||
1269 | * array gives the sta through container_of */ | ||
1270 | u8 *ptid = (u8 *)data; | ||
1271 | u8 *timer_to_id = ptid - *ptid; | ||
1272 | struct sta_info *sta = container_of(timer_to_id, struct sta_info, | ||
1273 | timer_to_tid[0]); | ||
1274 | |||
1275 | printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); | ||
1276 | ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid, | ||
1277 | WLAN_BACK_TIMER, | ||
1278 | WLAN_REASON_QSTA_TIMEOUT); | ||
1279 | } | ||
1280 | |||
1281 | |||
1083 | static void ieee80211_rx_mgmt_auth(struct net_device *dev, | 1282 | static void ieee80211_rx_mgmt_auth(struct net_device *dev, |
1084 | struct ieee80211_if_sta *ifsta, | 1283 | struct ieee80211_if_sta *ifsta, |
1085 | struct ieee80211_mgmt *mgmt, | 1284 | struct ieee80211_mgmt *mgmt, |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ffe8a49d8927..60ca07804056 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -104,6 +104,7 @@ static void sta_info_release(struct kref *kref) | |||
104 | struct sta_info *sta = container_of(kref, struct sta_info, kref); | 104 | struct sta_info *sta = container_of(kref, struct sta_info, kref); |
105 | struct ieee80211_local *local = sta->local; | 105 | struct ieee80211_local *local = sta->local; |
106 | struct sk_buff *skb; | 106 | struct sk_buff *skb; |
107 | int i; | ||
107 | 108 | ||
108 | /* free sta structure; it has already been removed from | 109 | /* free sta structure; it has already been removed from |
109 | * hash table etc. external structures. Make sure that all | 110 | * hash table etc. external structures. Make sure that all |
@@ -116,6 +117,8 @@ static void sta_info_release(struct kref *kref) | |||
116 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { | 117 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { |
117 | dev_kfree_skb_any(skb); | 118 | dev_kfree_skb_any(skb); |
118 | } | 119 | } |
120 | for (i = 0; i < STA_TID_NUM; i++) | ||
121 | del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer); | ||
119 | rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); | 122 | rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); |
120 | rate_control_put(sta->rate_ctrl); | 123 | rate_control_put(sta->rate_ctrl); |
121 | kfree(sta); | 124 | kfree(sta); |