diff options
author | Bartosz Markowski <bartosz.markowski@tieto.com> | 2013-09-26 11:47:12 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2013-09-27 07:58:14 -0400 |
commit | 5e00d31a0fb74c36f3b174ff0d4914cf09016e6f (patch) | |
tree | 9557fe332e411ef341be3fb11951bb5a7c9a8baa | |
parent | b3effe61a1a3b8f20fa4b4d30c4390a6b81a6fc2 (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.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_tx.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 70 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 51 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.h | 1 |
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 | |||
46 | struct ath10k; | 48 | struct ath10k; |
47 | 49 | ||
48 | struct ath10k_skb_cb { | 50 | struct 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) | |||
1486 | static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) | 1486 | static 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 | ||
1508 | exit: | 1524 | exit: |
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 | ||
1615 | void 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 | |||
1628 | void 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); | |||
34 | void ath10k_reset_scan(unsigned long ptr); | 34 | void ath10k_reset_scan(unsigned long ptr); |
35 | void ath10k_offchan_tx_purge(struct ath10k *ar); | 35 | void ath10k_offchan_tx_purge(struct ath10k *ar); |
36 | void ath10k_offchan_tx_work(struct work_struct *work); | 36 | void ath10k_offchan_tx_work(struct work_struct *work); |
37 | void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); | ||
38 | void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work); | ||
37 | void ath10k_halt(struct ath10k *ar); | 39 | void ath10k_halt(struct ath10k *ar); |
38 | 40 | ||
39 | static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) | 41 | static 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 | ||
413 | int 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 | |||
413 | static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) | 464 | static 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, | |||
3483 | int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); | 3483 | int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); |
3484 | int ath10k_wmi_force_fw_hang(struct ath10k *ar, | 3484 | int 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); |
3486 | int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); | ||
3486 | 3487 | ||
3487 | #endif /* _WMI_H_ */ | 3488 | #endif /* _WMI_H_ */ |