aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartosz Markowski <bartosz.markowski@tieto.com>2013-09-26 11:47:12 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2013-09-27 07:58:14 -0400
commit5e00d31a0fb74c36f3b174ff0d4914cf09016e6f (patch)
tree9557fe332e411ef341be3fb11951bb5a7c9a8baa
parentb3effe61a1a3b8f20fa4b4d30c4390a6b81a6fc2 (diff)
ath10k: bring back the WMI path for mgmt frames
This is still the only way to submit mgmt frames in case of 10.X firmware. This patch introduces wmi_mgmt_tx queue, because of the fact WMI command can block. This is a problem for ath10k_tx_htt(), since it's called from atomic context. The skb queue and worker are introduced to move the mgmt frame handling out of .tx callback context and not block. Signed-off-by: Bartosz Markowski <bartosz.markowski@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c3
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h10
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c70
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c51
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h1
7 files changed, 128 insertions, 13 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 31860a60c538..bd74dacad1ed 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -520,6 +520,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
520 INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); 520 INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
521 skb_queue_head_init(&ar->offchan_tx_queue); 521 skb_queue_head_init(&ar->offchan_tx_queue);
522 522
523 INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
524 skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
525
523 init_waitqueue_head(&ar->event_queue); 526 init_waitqueue_head(&ar->event_queue);
524 527
525 INIT_WORK(&ar->restart_work, ath10k_core_restart); 528 INIT_WORK(&ar->restart_work, ath10k_core_restart);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index e2a2658cf841..984db115cb80 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -43,15 +43,17 @@
43/* Antenna noise floor */ 43/* Antenna noise floor */
44#define ATH10K_DEFAULT_NOISE_FLOOR -95 44#define ATH10K_DEFAULT_NOISE_FLOOR -95
45 45
46#define ATH10K_MAX_NUM_MGMT_PENDING 16
47
46struct ath10k; 48struct ath10k;
47 49
48struct ath10k_skb_cb { 50struct ath10k_skb_cb {
49 dma_addr_t paddr; 51 dma_addr_t paddr;
50 bool is_mapped; 52 bool is_mapped;
51 bool is_aborted; 53 bool is_aborted;
54 u8 vdev_id;
52 55
53 struct { 56 struct {
54 u8 vdev_id;
55 u8 tid; 57 u8 tid;
56 bool is_offchan; 58 bool is_offchan;
57 59
@@ -284,6 +286,9 @@ enum ath10k_fw_features {
284 /* firmware from 10X branch */ 286 /* firmware from 10X branch */
285 ATH10K_FW_FEATURE_WMI_10X = 1, 287 ATH10K_FW_FEATURE_WMI_10X = 1,
286 288
289 /* firmware support tx frame management over WMI, otherwise it's HTT */
290 ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,
291
287 /* keep last */ 292 /* keep last */
288 ATH10K_FW_FEATURE_COUNT, 293 ATH10K_FW_FEATURE_COUNT,
289}; 294};
@@ -393,6 +398,9 @@ struct ath10k {
393 struct completion offchan_tx_completed; 398 struct completion offchan_tx_completed;
394 struct sk_buff *offchan_tx_skb; 399 struct sk_buff *offchan_tx_skb;
395 400
401 struct work_struct wmi_mgmt_tx_work;
402 struct sk_buff_head wmi_mgmt_tx_queue;
403
396 enum ath10k_state state; 404 enum ath10k_state state;
397 405
398 struct work_struct restart_work; 406 struct work_struct restart_work;
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 3b93c6a01c6c..d9335e9d0d04 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -308,7 +308,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
308 struct sk_buff *txdesc = NULL; 308 struct sk_buff *txdesc = NULL;
309 struct htt_cmd *cmd; 309 struct htt_cmd *cmd;
310 struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); 310 struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
311 u8 vdev_id = skb_cb->htt.vdev_id; 311 u8 vdev_id = skb_cb->vdev_id;
312 int len = 0; 312 int len = 0;
313 int msdu_id = -1; 313 int msdu_id = -1;
314 int res; 314 int res;
@@ -384,7 +384,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
384 struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); 384 struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
385 struct sk_buff *txdesc = NULL; 385 struct sk_buff *txdesc = NULL;
386 bool use_frags; 386 bool use_frags;
387 u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; 387 u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id;
388 u8 tid; 388 u8 tid;
389 int prefetch_len, desc_len; 389 int prefetch_len, desc_len;
390 int msdu_id = -1; 390 int msdu_id = -1;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index df4d29981c4b..f0bc707257a5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1486,7 +1486,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
1486static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) 1486static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
1487{ 1487{
1488 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 1488 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1489 int ret; 1489 int ret = 0;
1490 1490
1491 if (ar->htt.target_version_major >= 3) { 1491 if (ar->htt.target_version_major >= 3) {
1492 /* Since HTT 3.0 there is no separate mgmt tx command */ 1492 /* Since HTT 3.0 there is no separate mgmt tx command */
@@ -1494,16 +1494,32 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
1494 goto exit; 1494 goto exit;
1495 } 1495 }
1496 1496
1497 if (ieee80211_is_mgmt(hdr->frame_control)) 1497 if (ieee80211_is_mgmt(hdr->frame_control)) {
1498 ret = ath10k_htt_mgmt_tx(&ar->htt, skb); 1498 if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
1499 else if (ieee80211_is_nullfunc(hdr->frame_control)) 1499 ar->fw_features)) {
1500 if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
1501 ATH10K_MAX_NUM_MGMT_PENDING) {
1502 ath10k_warn("wmi mgmt_tx queue limit reached\n");
1503 ret = -EBUSY;
1504 goto exit;
1505 }
1506
1507 skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
1508 ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
1509 } else {
1510 ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
1511 }
1512 } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
1513 ar->fw_features) &&
1514 ieee80211_is_nullfunc(hdr->frame_control)) {
1500 /* FW does not report tx status properly for NullFunc frames 1515 /* FW does not report tx status properly for NullFunc frames
1501 * unless they are sent through mgmt tx path. mac80211 sends 1516 * unless they are sent through mgmt tx path. mac80211 sends
1502 * those frames when it detects link/beacon loss and depends on 1517 * those frames when it detects link/beacon loss and depends
1503 * the tx status to be correct. */ 1518 * on the tx status to be correct. */
1504 ret = ath10k_htt_mgmt_tx(&ar->htt, skb); 1519 ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
1505 else 1520 } else {
1506 ret = ath10k_htt_tx(&ar->htt, skb); 1521 ret = ath10k_htt_tx(&ar->htt, skb);
1522 }
1507 1523
1508exit: 1524exit:
1509 if (ret) { 1525 if (ret) {
@@ -1554,7 +1570,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
1554 1570
1555 hdr = (struct ieee80211_hdr *)skb->data; 1571 hdr = (struct ieee80211_hdr *)skb->data;
1556 peer_addr = ieee80211_get_DA(hdr); 1572 peer_addr = ieee80211_get_DA(hdr);
1557 vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; 1573 vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
1558 1574
1559 spin_lock_bh(&ar->data_lock); 1575 spin_lock_bh(&ar->data_lock);
1560 peer = ath10k_peer_find(ar, vdev_id, peer_addr); 1576 peer = ath10k_peer_find(ar, vdev_id, peer_addr);
@@ -1596,6 +1612,36 @@ void ath10k_offchan_tx_work(struct work_struct *work)
1596 } 1612 }
1597} 1613}
1598 1614
1615void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
1616{
1617 struct sk_buff *skb;
1618
1619 for (;;) {
1620 skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
1621 if (!skb)
1622 break;
1623
1624 ieee80211_free_txskb(ar->hw, skb);
1625 }
1626}
1627
1628void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
1629{
1630 struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
1631 struct sk_buff *skb;
1632 int ret;
1633
1634 for (;;) {
1635 skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
1636 if (!skb)
1637 break;
1638
1639 ret = ath10k_wmi_mgmt_tx(ar, skb);
1640 if (ret)
1641 ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
1642 }
1643}
1644
1599/************/ 1645/************/
1600/* Scanning */ 1646/* Scanning */
1601/************/ 1647/************/
@@ -1754,14 +1800,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
1754 ath10k_tx_h_seq_no(skb); 1800 ath10k_tx_h_seq_no(skb);
1755 } 1801 }
1756 1802
1803 ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
1757 ATH10K_SKB_CB(skb)->htt.is_offchan = false; 1804 ATH10K_SKB_CB(skb)->htt.is_offchan = false;
1758 ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
1759 ATH10K_SKB_CB(skb)->htt.tid = tid; 1805 ATH10K_SKB_CB(skb)->htt.tid = tid;
1760 1806
1761 if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { 1807 if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
1762 spin_lock_bh(&ar->data_lock); 1808 spin_lock_bh(&ar->data_lock);
1763 ATH10K_SKB_CB(skb)->htt.is_offchan = true; 1809 ATH10K_SKB_CB(skb)->htt.is_offchan = true;
1764 ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; 1810 ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
1765 spin_unlock_bh(&ar->data_lock); 1811 spin_unlock_bh(&ar->data_lock);
1766 1812
1767 ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); 1813 ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
@@ -1783,6 +1829,7 @@ void ath10k_halt(struct ath10k *ar)
1783 1829
1784 del_timer_sync(&ar->scan.timeout); 1830 del_timer_sync(&ar->scan.timeout);
1785 ath10k_offchan_tx_purge(ar); 1831 ath10k_offchan_tx_purge(ar);
1832 ath10k_mgmt_over_wmi_tx_purge(ar);
1786 ath10k_peer_cleanup_all(ar); 1833 ath10k_peer_cleanup_all(ar);
1787 ath10k_core_stop(ar); 1834 ath10k_core_stop(ar);
1788 ath10k_hif_power_down(ar); 1835 ath10k_hif_power_down(ar);
@@ -1859,7 +1906,10 @@ static void ath10k_stop(struct ieee80211_hw *hw)
1859 ar->state = ATH10K_STATE_OFF; 1906 ar->state = ATH10K_STATE_OFF;
1860 mutex_unlock(&ar->conf_mutex); 1907 mutex_unlock(&ar->conf_mutex);
1861 1908
1909 ath10k_mgmt_over_wmi_tx_purge(ar);
1910
1862 cancel_work_sync(&ar->offchan_tx_work); 1911 cancel_work_sync(&ar->offchan_tx_work);
1912 cancel_work_sync(&ar->wmi_mgmt_tx_work);
1863 cancel_work_sync(&ar->restart_work); 1913 cancel_work_sync(&ar->restart_work);
1864} 1914}
1865 1915
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 6fce9bfb19a5..ba1021997b8f 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
34void ath10k_reset_scan(unsigned long ptr); 34void ath10k_reset_scan(unsigned long ptr);
35void ath10k_offchan_tx_purge(struct ath10k *ar); 35void ath10k_offchan_tx_purge(struct ath10k *ar);
36void ath10k_offchan_tx_work(struct work_struct *work); 36void ath10k_offchan_tx_work(struct work_struct *work);
37void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
38void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
37void ath10k_halt(struct ath10k *ar); 39void ath10k_halt(struct ath10k *ar);
38 40
39static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) 41static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 97ba23e78abf..8c223671ecc4 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -410,6 +410,57 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
410 return ret; 410 return ret;
411} 411}
412 412
413int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
414{
415 int ret = 0;
416 struct wmi_mgmt_tx_cmd *cmd;
417 struct ieee80211_hdr *hdr;
418 struct sk_buff *wmi_skb;
419 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
420 int len;
421 u16 fc;
422
423 hdr = (struct ieee80211_hdr *)skb->data;
424 fc = le16_to_cpu(hdr->frame_control);
425
426 if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
427 return -EINVAL;
428
429 len = sizeof(cmd->hdr) + skb->len;
430 len = round_up(len, 4);
431
432 wmi_skb = ath10k_wmi_alloc_skb(len);
433 if (!wmi_skb)
434 return -ENOMEM;
435
436 cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
437
438 cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
439 cmd->hdr.tx_rate = 0;
440 cmd->hdr.tx_power = 0;
441 cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
442
443 memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
444 memcpy(cmd->buf, skb->data, skb->len);
445
446 ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
447 wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
448 fc & IEEE80211_FCTL_STYPE);
449
450 /* Send the management frame buffer to the target */
451 ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
452 if (ret) {
453 dev_kfree_skb_any(skb);
454 return ret;
455 }
456
457 /* TODO: report tx status to mac80211 - temporary just ACK */
458 info->flags |= IEEE80211_TX_STAT_ACK;
459 ieee80211_tx_status_irqsafe(ar->hw, skb);
460
461 return ret;
462}
463
413static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) 464static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
414{ 465{
415 struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; 466 struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 56339d2e338a..1c515d682285 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -3483,5 +3483,6 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
3483int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); 3483int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
3484int ath10k_wmi_force_fw_hang(struct ath10k *ar, 3484int ath10k_wmi_force_fw_hang(struct ath10k *ar,
3485 enum wmi_force_fw_hang_type type, u32 delay_ms); 3485 enum wmi_force_fw_hang_type type, u32 delay_ms);
3486int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
3486 3487
3487#endif /* _WMI_H_ */ 3488#endif /* _WMI_H_ */