0001
0002
0003
0004
0005
0006
0007
0008 #include <net/mac80211.h>
0009 #include <linux/etherdevice.h>
0010
0011 #include "data_tx.h"
0012 #include "wfx.h"
0013 #include "bh.h"
0014 #include "sta.h"
0015 #include "queue.h"
0016 #include "debug.h"
0017 #include "traces.h"
0018 #include "hif_tx_mib.h"
0019
0020 static int wfx_get_hw_rate(struct wfx_dev *wdev, const struct ieee80211_tx_rate *rate)
0021 {
0022 struct ieee80211_supported_band *band;
0023
0024 if (rate->idx < 0)
0025 return -1;
0026 if (rate->flags & IEEE80211_TX_RC_MCS) {
0027 if (rate->idx > 7) {
0028 WARN(1, "wrong rate->idx value: %d", rate->idx);
0029 return -1;
0030 }
0031 return rate->idx + 14;
0032 }
0033
0034
0035
0036 band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ];
0037 if (rate->idx >= band->n_bitrates) {
0038 WARN(1, "wrong rate->idx value: %d", rate->idx);
0039 return -1;
0040 }
0041 return band->bitrates[rate->idx].hw_value;
0042 }
0043
0044
0045
0046 static void wfx_tx_policy_build(struct wfx_vif *wvif, struct wfx_tx_policy *policy,
0047 struct ieee80211_tx_rate *rates)
0048 {
0049 struct wfx_dev *wdev = wvif->wdev;
0050 int i, rateid;
0051 u8 count;
0052
0053 WARN(rates[0].idx < 0, "invalid rate policy");
0054 memset(policy, 0, sizeof(*policy));
0055 for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
0056 if (rates[i].idx < 0)
0057 break;
0058 WARN_ON(rates[i].count > 15);
0059 rateid = wfx_get_hw_rate(wdev, &rates[i]);
0060
0061 count = rates[i].count;
0062 if (rateid % 2)
0063 count <<= 4;
0064 policy->rates[rateid / 2] |= count;
0065 }
0066 }
0067
0068 static bool wfx_tx_policy_is_equal(const struct wfx_tx_policy *a, const struct wfx_tx_policy *b)
0069 {
0070 return !memcmp(a->rates, b->rates, sizeof(a->rates));
0071 }
0072
0073 static int wfx_tx_policy_find(struct wfx_tx_policy_cache *cache, struct wfx_tx_policy *wanted)
0074 {
0075 struct wfx_tx_policy *it;
0076
0077 list_for_each_entry(it, &cache->used, link)
0078 if (wfx_tx_policy_is_equal(wanted, it))
0079 return it - cache->cache;
0080 list_for_each_entry(it, &cache->free, link)
0081 if (wfx_tx_policy_is_equal(wanted, it))
0082 return it - cache->cache;
0083 return -1;
0084 }
0085
0086 static void wfx_tx_policy_use(struct wfx_tx_policy_cache *cache, struct wfx_tx_policy *entry)
0087 {
0088 ++entry->usage_count;
0089 list_move(&entry->link, &cache->used);
0090 }
0091
0092 static int wfx_tx_policy_release(struct wfx_tx_policy_cache *cache, struct wfx_tx_policy *entry)
0093 {
0094 int ret = --entry->usage_count;
0095
0096 if (!ret)
0097 list_move(&entry->link, &cache->free);
0098 return ret;
0099 }
0100
0101 static int wfx_tx_policy_get(struct wfx_vif *wvif, struct ieee80211_tx_rate *rates, bool *renew)
0102 {
0103 int idx;
0104 struct wfx_tx_policy_cache *cache = &wvif->tx_policy_cache;
0105 struct wfx_tx_policy wanted;
0106 struct wfx_tx_policy *entry;
0107
0108 wfx_tx_policy_build(wvif, &wanted, rates);
0109
0110 spin_lock_bh(&cache->lock);
0111 if (list_empty(&cache->free)) {
0112 WARN(1, "unable to get a valid Tx policy");
0113 spin_unlock_bh(&cache->lock);
0114 return HIF_TX_RETRY_POLICY_INVALID;
0115 }
0116 idx = wfx_tx_policy_find(cache, &wanted);
0117 if (idx >= 0) {
0118 *renew = false;
0119 } else {
0120
0121 *renew = true;
0122 entry = list_entry(cache->free.prev, struct wfx_tx_policy, link);
0123 memcpy(entry->rates, wanted.rates, sizeof(entry->rates));
0124 entry->uploaded = false;
0125 entry->usage_count = 0;
0126 idx = entry - cache->cache;
0127 }
0128 wfx_tx_policy_use(cache, &cache->cache[idx]);
0129 if (list_empty(&cache->free))
0130 ieee80211_stop_queues(wvif->wdev->hw);
0131 spin_unlock_bh(&cache->lock);
0132 return idx;
0133 }
0134
0135 static void wfx_tx_policy_put(struct wfx_vif *wvif, int idx)
0136 {
0137 int usage, locked;
0138 struct wfx_tx_policy_cache *cache = &wvif->tx_policy_cache;
0139
0140 if (idx == HIF_TX_RETRY_POLICY_INVALID)
0141 return;
0142 spin_lock_bh(&cache->lock);
0143 locked = list_empty(&cache->free);
0144 usage = wfx_tx_policy_release(cache, &cache->cache[idx]);
0145 if (locked && !usage)
0146 ieee80211_wake_queues(wvif->wdev->hw);
0147 spin_unlock_bh(&cache->lock);
0148 }
0149
0150 static int wfx_tx_policy_upload(struct wfx_vif *wvif)
0151 {
0152 struct wfx_tx_policy *policies = wvif->tx_policy_cache.cache;
0153 u8 tmp_rates[12];
0154 int i, is_used;
0155
0156 do {
0157 spin_lock_bh(&wvif->tx_policy_cache.lock);
0158 for (i = 0; i < ARRAY_SIZE(wvif->tx_policy_cache.cache); ++i) {
0159 is_used = memzcmp(policies[i].rates, sizeof(policies[i].rates));
0160 if (!policies[i].uploaded && is_used)
0161 break;
0162 }
0163 if (i < ARRAY_SIZE(wvif->tx_policy_cache.cache)) {
0164 policies[i].uploaded = true;
0165 memcpy(tmp_rates, policies[i].rates, sizeof(tmp_rates));
0166 spin_unlock_bh(&wvif->tx_policy_cache.lock);
0167 wfx_hif_set_tx_rate_retry_policy(wvif, i, tmp_rates);
0168 } else {
0169 spin_unlock_bh(&wvif->tx_policy_cache.lock);
0170 }
0171 } while (i < ARRAY_SIZE(wvif->tx_policy_cache.cache));
0172 return 0;
0173 }
0174
0175 void wfx_tx_policy_upload_work(struct work_struct *work)
0176 {
0177 struct wfx_vif *wvif = container_of(work, struct wfx_vif, tx_policy_upload_work);
0178
0179 wfx_tx_policy_upload(wvif);
0180 wfx_tx_unlock(wvif->wdev);
0181 }
0182
0183 void wfx_tx_policy_init(struct wfx_vif *wvif)
0184 {
0185 struct wfx_tx_policy_cache *cache = &wvif->tx_policy_cache;
0186 int i;
0187
0188 memset(cache, 0, sizeof(*cache));
0189
0190 spin_lock_init(&cache->lock);
0191 INIT_LIST_HEAD(&cache->used);
0192 INIT_LIST_HEAD(&cache->free);
0193
0194 for (i = 0; i < ARRAY_SIZE(cache->cache); ++i)
0195 list_add(&cache->cache[i].link, &cache->free);
0196 }
0197
0198
0199
0200 static bool wfx_is_action_back(struct ieee80211_hdr *hdr)
0201 {
0202 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr;
0203
0204 if (!ieee80211_is_action(mgmt->frame_control))
0205 return false;
0206 if (mgmt->u.action.category != WLAN_CATEGORY_BACK)
0207 return false;
0208 return true;
0209 }
0210
0211 static u8 wfx_tx_get_link_id(struct wfx_vif *wvif, struct ieee80211_sta *sta,
0212 struct ieee80211_hdr *hdr)
0213 {
0214 struct wfx_sta_priv *sta_priv = sta ? (struct wfx_sta_priv *)&sta->drv_priv : NULL;
0215 struct ieee80211_vif *vif = wvif_to_vif(wvif);
0216 const u8 *da = ieee80211_get_DA(hdr);
0217
0218 if (sta_priv && sta_priv->link_id)
0219 return sta_priv->link_id;
0220 if (vif->type != NL80211_IFTYPE_AP)
0221 return 0;
0222 if (is_multicast_ether_addr(da))
0223 return 0;
0224 return HIF_LINK_ID_NOT_ASSOCIATED;
0225 }
0226
0227 static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates)
0228 {
0229 int i;
0230 bool finished;
0231
0232
0233 for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
0234 if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
0235 rates[i].flags |= IEEE80211_TX_RC_SHORT_GI;
0236 if (!(rates[0].flags & IEEE80211_TX_RC_SHORT_GI))
0237 rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI;
0238 if (!(rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS))
0239 rates[i].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS;
0240 }
0241
0242
0243 do {
0244 finished = true;
0245 for (i = 0; i < IEEE80211_TX_MAX_RATES - 1; i++) {
0246 if (rates[i + 1].idx == rates[i].idx &&
0247 rates[i].idx != -1) {
0248 rates[i].count += rates[i + 1].count;
0249 if (rates[i].count > 15)
0250 rates[i].count = 15;
0251 rates[i + 1].idx = -1;
0252 rates[i + 1].count = 0;
0253
0254 finished = false;
0255 }
0256 if (rates[i + 1].idx > rates[i].idx) {
0257 swap(rates[i + 1], rates[i]);
0258 finished = false;
0259 }
0260 }
0261 } while (!finished);
0262
0263 for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
0264 if (rates[i].idx == 0)
0265 break;
0266 if (rates[i].idx == -1) {
0267 rates[i].idx = 0;
0268 rates[i].count = 8;
0269 rates[i].flags = rates[i - 1].flags & IEEE80211_TX_RC_MCS;
0270 break;
0271 }
0272 }
0273
0274 for (i = 1; i < IEEE80211_TX_MAX_RATES; i++)
0275 rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI;
0276 }
0277
0278 static u8 wfx_tx_get_retry_policy_id(struct wfx_vif *wvif, struct ieee80211_tx_info *tx_info)
0279 {
0280 bool tx_policy_renew = false;
0281 u8 ret;
0282
0283 ret = wfx_tx_policy_get(wvif, tx_info->driver_rates, &tx_policy_renew);
0284 if (ret == HIF_TX_RETRY_POLICY_INVALID)
0285 dev_warn(wvif->wdev->dev, "unable to get a valid Tx policy");
0286
0287 if (tx_policy_renew) {
0288 wfx_tx_lock(wvif->wdev);
0289 if (!schedule_work(&wvif->tx_policy_upload_work))
0290 wfx_tx_unlock(wvif->wdev);
0291 }
0292 return ret;
0293 }
0294
0295 static int wfx_tx_get_frame_format(struct ieee80211_tx_info *tx_info)
0296 {
0297 if (!(tx_info->driver_rates[0].flags & IEEE80211_TX_RC_MCS))
0298 return HIF_FRAME_FORMAT_NON_HT;
0299 else if (!(tx_info->driver_rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD))
0300 return HIF_FRAME_FORMAT_MIXED_FORMAT_HT;
0301 else
0302 return HIF_FRAME_FORMAT_GF_HT_11N;
0303 }
0304
0305 static int wfx_tx_get_icv_len(struct ieee80211_key_conf *hw_key)
0306 {
0307 int mic_space;
0308
0309 if (!hw_key)
0310 return 0;
0311 if (hw_key->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
0312 return 0;
0313 mic_space = (hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) ? 8 : 0;
0314 return hw_key->icv_len + mic_space;
0315 }
0316
0317 static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct sk_buff *skb)
0318 {
0319 struct wfx_hif_msg *hif_msg;
0320 struct wfx_hif_req_tx *req;
0321 struct wfx_tx_priv *tx_priv;
0322 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
0323 struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
0324 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
0325 int queue_id = skb_get_queue_mapping(skb);
0326 size_t offset = (size_t)skb->data & 3;
0327 int wmsg_len = sizeof(struct wfx_hif_msg) + sizeof(struct wfx_hif_req_tx) + offset;
0328
0329 WARN(queue_id >= IEEE80211_NUM_ACS, "unsupported queue_id");
0330 wfx_tx_fixup_rates(tx_info->driver_rates);
0331
0332
0333 memset(tx_info->rate_driver_data, 0, sizeof(struct wfx_tx_priv));
0334
0335 tx_priv = (struct wfx_tx_priv *)tx_info->rate_driver_data;
0336 tx_priv->icv_size = wfx_tx_get_icv_len(hw_key);
0337
0338
0339 WARN(skb_headroom(skb) < wmsg_len, "not enough space in skb");
0340 WARN(offset & 1, "attempt to transmit an unaligned frame");
0341 skb_put(skb, tx_priv->icv_size);
0342 skb_push(skb, wmsg_len);
0343 memset(skb->data, 0, wmsg_len);
0344 hif_msg = (struct wfx_hif_msg *)skb->data;
0345 hif_msg->len = cpu_to_le16(skb->len);
0346 hif_msg->id = HIF_REQ_ID_TX;
0347 hif_msg->interface = wvif->id;
0348 if (skb->len > le16_to_cpu(wvif->wdev->hw_caps.size_inp_ch_buf)) {
0349 dev_warn(wvif->wdev->dev,
0350 "requested frame size (%d) is larger than maximum supported (%d)\n",
0351 skb->len, le16_to_cpu(wvif->wdev->hw_caps.size_inp_ch_buf));
0352 skb_pull(skb, wmsg_len);
0353 return -EIO;
0354 }
0355
0356
0357 req = (struct wfx_hif_req_tx *)hif_msg->body;
0358
0359
0360
0361 req->packet_id = atomic_add_return(1, &wvif->wdev->packet_id) & 0xFFFF;
0362 req->packet_id |= IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)) << 16;
0363 req->packet_id |= queue_id << 28;
0364
0365 req->fc_offset = offset;
0366
0367 req->queue_id = 3 - queue_id;
0368 req->peer_sta_id = wfx_tx_get_link_id(wvif, sta, hdr);
0369 req->retry_policy_index = wfx_tx_get_retry_policy_id(wvif, tx_info);
0370 req->frame_format = wfx_tx_get_frame_format(tx_info);
0371 if (tx_info->driver_rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
0372 req->short_gi = 1;
0373 if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
0374 req->after_dtim = 1;
0375
0376
0377 wfx_tx_queues_put(wvif, skb);
0378 if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
0379 schedule_work(&wvif->update_tim_work);
0380 wfx_bh_request_tx(wvif->wdev);
0381 return 0;
0382 }
0383
0384 void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb)
0385 {
0386 struct wfx_dev *wdev = hw->priv;
0387 struct wfx_vif *wvif;
0388 struct ieee80211_sta *sta = control ? control->sta : NULL;
0389 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
0390 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
0391 size_t driver_data_room = sizeof_field(struct ieee80211_tx_info, rate_driver_data);
0392
0393 BUILD_BUG_ON_MSG(sizeof(struct wfx_tx_priv) > driver_data_room,
0394 "struct tx_priv is too large");
0395 WARN(skb->next || skb->prev, "skb is already member of a list");
0396
0397 if (tx_info->control.vif)
0398 wvif = (struct wfx_vif *)tx_info->control.vif->drv_priv;
0399 else
0400 wvif = wvif_iterate(wdev, NULL);
0401 if (WARN_ON(!wvif))
0402 goto drop;
0403
0404
0405
0406 if (wfx_is_action_back(hdr)) {
0407 dev_info(wdev->dev, "drop BA action\n");
0408 goto drop;
0409 }
0410 if (wfx_tx_inner(wvif, sta, skb))
0411 goto drop;
0412
0413 return;
0414
0415 drop:
0416 ieee80211_tx_status_irqsafe(wdev->hw, skb);
0417 }
0418
0419 static void wfx_skb_dtor(struct wfx_vif *wvif, struct sk_buff *skb)
0420 {
0421 struct wfx_hif_msg *hif = (struct wfx_hif_msg *)skb->data;
0422 struct wfx_hif_req_tx *req = (struct wfx_hif_req_tx *)hif->body;
0423 unsigned int offset = sizeof(struct wfx_hif_msg) + sizeof(struct wfx_hif_req_tx) +
0424 req->fc_offset;
0425
0426 if (!wvif) {
0427 pr_warn("vif associated with the skb does not exist anymore\n");
0428 return;
0429 }
0430 wfx_tx_policy_put(wvif, req->retry_policy_index);
0431 skb_pull(skb, offset);
0432 ieee80211_tx_status_irqsafe(wvif->wdev->hw, skb);
0433 }
0434
0435 static void wfx_tx_fill_rates(struct wfx_dev *wdev, struct ieee80211_tx_info *tx_info,
0436 const struct wfx_hif_cnf_tx *arg)
0437 {
0438 struct ieee80211_tx_rate *rate;
0439 int tx_count;
0440 int i;
0441
0442 tx_count = arg->ack_failures;
0443 if (!arg->status || arg->ack_failures)
0444 tx_count += 1;
0445 for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
0446 rate = &tx_info->status.rates[i];
0447 if (rate->idx < 0)
0448 break;
0449 if (tx_count < rate->count && arg->status == HIF_STATUS_TX_FAIL_RETRIES &&
0450 arg->ack_failures)
0451 dev_dbg(wdev->dev, "all retries were not consumed: %d != %d\n",
0452 rate->count, tx_count);
0453 if (tx_count <= rate->count && tx_count &&
0454 arg->txed_rate != wfx_get_hw_rate(wdev, rate))
0455 dev_dbg(wdev->dev, "inconsistent tx_info rates: %d != %d\n",
0456 arg->txed_rate, wfx_get_hw_rate(wdev, rate));
0457 if (tx_count > rate->count) {
0458 tx_count -= rate->count;
0459 } else if (!tx_count) {
0460 rate->count = 0;
0461 rate->idx = -1;
0462 } else {
0463 rate->count = tx_count;
0464 tx_count = 0;
0465 }
0466 }
0467 if (tx_count)
0468 dev_dbg(wdev->dev, "%d more retries than expected\n", tx_count);
0469 }
0470
0471 void wfx_tx_confirm_cb(struct wfx_dev *wdev, const struct wfx_hif_cnf_tx *arg)
0472 {
0473 const struct wfx_tx_priv *tx_priv;
0474 struct ieee80211_tx_info *tx_info;
0475 struct wfx_vif *wvif;
0476 struct sk_buff *skb;
0477
0478 skb = wfx_pending_get(wdev, arg->packet_id);
0479 if (!skb) {
0480 dev_warn(wdev->dev, "received unknown packet_id (%#.8x) from chip\n",
0481 arg->packet_id);
0482 return;
0483 }
0484 tx_info = IEEE80211_SKB_CB(skb);
0485 tx_priv = wfx_skb_tx_priv(skb);
0486 wvif = wdev_to_wvif(wdev, ((struct wfx_hif_msg *)skb->data)->interface);
0487 WARN_ON(!wvif);
0488 if (!wvif)
0489 return;
0490
0491
0492 _trace_tx_stats(arg, skb, wfx_pending_get_pkt_us_delay(wdev, skb));
0493 wfx_tx_fill_rates(wdev, tx_info, arg);
0494 skb_trim(skb, skb->len - tx_priv->icv_size);
0495
0496
0497
0498 memset(tx_info->rate_driver_data, 0, sizeof(tx_info->rate_driver_data));
0499 memset(tx_info->pad, 0, sizeof(tx_info->pad));
0500
0501 if (!arg->status) {
0502 tx_info->status.tx_time = le32_to_cpu(arg->media_delay) -
0503 le32_to_cpu(arg->tx_queue_delay);
0504 if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
0505 tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
0506 else
0507 tx_info->flags |= IEEE80211_TX_STAT_ACK;
0508 } else if (arg->status == HIF_STATUS_TX_FAIL_REQUEUE) {
0509 WARN(!arg->requeue, "incoherent status and result_flags");
0510 if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
0511 wvif->after_dtim_tx_allowed = false;
0512 schedule_work(&wvif->update_tim_work);
0513 }
0514 tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
0515 }
0516 wfx_skb_dtor(wvif, skb);
0517 }
0518
0519 static void wfx_flush_vif(struct wfx_vif *wvif, u32 queues, struct sk_buff_head *dropped)
0520 {
0521 struct wfx_queue *queue;
0522 int i;
0523
0524 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
0525 if (!(BIT(i) & queues))
0526 continue;
0527 queue = &wvif->tx_queue[i];
0528 if (dropped)
0529 wfx_tx_queue_drop(wvif, queue, dropped);
0530 }
0531 if (wvif->wdev->chip_frozen)
0532 return;
0533 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
0534 if (!(BIT(i) & queues))
0535 continue;
0536 queue = &wvif->tx_queue[i];
0537 if (wait_event_timeout(wvif->wdev->tx_dequeue, wfx_tx_queue_empty(wvif, queue),
0538 msecs_to_jiffies(1000)) <= 0)
0539 dev_warn(wvif->wdev->dev, "frames queued while flushing tx queues?");
0540 }
0541 }
0542
0543 void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop)
0544 {
0545 struct wfx_dev *wdev = hw->priv;
0546 struct sk_buff_head dropped;
0547 struct wfx_vif *wvif;
0548 struct wfx_hif_msg *hif;
0549 struct sk_buff *skb;
0550
0551 skb_queue_head_init(&dropped);
0552 if (vif) {
0553 wvif = (struct wfx_vif *)vif->drv_priv;
0554 wfx_flush_vif(wvif, queues, drop ? &dropped : NULL);
0555 } else {
0556 wvif = NULL;
0557 while ((wvif = wvif_iterate(wdev, wvif)) != NULL)
0558 wfx_flush_vif(wvif, queues, drop ? &dropped : NULL);
0559 }
0560 wfx_tx_flush(wdev);
0561 if (wdev->chip_frozen)
0562 wfx_pending_drop(wdev, &dropped);
0563 while ((skb = skb_dequeue(&dropped)) != NULL) {
0564 hif = (struct wfx_hif_msg *)skb->data;
0565 wvif = wdev_to_wvif(wdev, hif->interface);
0566 ieee80211_tx_info_clear_status(IEEE80211_SKB_CB(skb));
0567 wfx_skb_dtor(wvif, skb);
0568 }
0569 }