diff options
author | Jérôme Pouiller <jerome.pouiller@silabs.com> | 2019-09-19 14:25:45 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-10-04 10:47:35 +0200 |
commit | 9bca45f3d6924f19f29c0d019e961af3f41bdc9e (patch) | |
tree | ad4801c513155558e7e636c734d15d2cce6214fd /drivers/staging/wfx/sta.c | |
parent | staging: wfx: add debug files and trace debug events (diff) | |
download | linux-dev-9bca45f3d6924f19f29c0d019e961af3f41bdc9e.tar.xz linux-dev-9bca45f3d6924f19f29c0d019e961af3f41bdc9e.zip |
staging: wfx: allow to send 802.11 frames
Three things make this task more complex than it should:
- Chip necessitate to associate a link-id to each station. It is same
thing than association ID but, using 8 bits only.
- Rate policy is sent separately from Tx frames
- Driver try to handle itself power saving of stations and multicast
data
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-17-Jerome.Pouiller@silabs.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/wfx/sta.c')
-rw-r--r-- | drivers/staging/wfx/sta.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index fe3ff6536a87..5714aba1432c 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -10,11 +10,123 @@ #include "sta.h" #include "wfx.h" +#define TXOP_UNIT 32 + +static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct hif_ie_flags target_frame = { + .beacon = 1, + }; + u16 tim_offset, tim_length; + u8 *tim_ptr; + + skb = ieee80211_beacon_get_tim(wvif->wdev->hw, wvif->vif, + &tim_offset, &tim_length); + if (!skb) + return -ENOENT; + tim_ptr = skb->data + tim_offset; + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + tim_ptr[2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + tim_ptr[4] |= 1; + else + tim_ptr[4] &= ~1; + } + + hif_update_ie(wvif, &target_frame, tim_ptr, tim_length); + dev_kfree_skb(skb); + + return 0; +} + +static void wfx_mcast_start_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_start_work); + + cancel_work_sync(&wvif->mcast_stop_work); + if (!wvif->aid0_bit_set) { + wfx_tx_lock_flush(wvif->wdev); + wfx_set_tim_impl(wvif, true); + wvif->aid0_bit_set = true; + mod_timer(&wvif->mcast_timeout, TU_TO_JIFFIES(1000)); + wfx_tx_unlock(wvif->wdev); + } +} + +static void wfx_mcast_stop_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_stop_work); + + if (wvif->aid0_bit_set) { + del_timer_sync(&wvif->mcast_timeout); + wfx_tx_lock_flush(wvif->wdev); + wvif->aid0_bit_set = false; + wfx_set_tim_impl(wvif, false); + wfx_tx_unlock(wvif->wdev); + } +} + +static void wfx_mcast_timeout(struct timer_list *t) +{ + struct wfx_vif *wvif = from_timer(wvif, t, mcast_timeout); + + dev_warn(wvif->wdev->dev, "multicast delivery timeout\n"); + spin_lock_bh(&wvif->ps_state_lock); + wvif->mcast_tx = wvif->aid0_bit_set && wvif->mcast_buffered; + if (wvif->mcast_tx) + wfx_bh_request_tx(wvif->wdev); + spin_unlock_bh(&wvif->ps_state_lock); +} + int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int i; struct wfx_dev *wdev = hw->priv; struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + // FIXME: parameters are set by kernel juste after interface_add. + // Keep struct hif_req_edca_queue_params blank? + struct hif_req_edca_queue_params default_edca_params[] = { + [IEEE80211_AC_VO] = { + .queue_id = HIF_QUEUE_ID_VOICE, + .aifsn = 2, + .cw_min = 3, + .cw_max = 7, + .tx_op_limit = TXOP_UNIT * 47, + }, + [IEEE80211_AC_VI] = { + .queue_id = HIF_QUEUE_ID_VIDEO, + .aifsn = 2, + .cw_min = 7, + .cw_max = 15, + .tx_op_limit = TXOP_UNIT * 94, + }, + [IEEE80211_AC_BE] = { + .queue_id = HIF_QUEUE_ID_BESTEFFORT, + .aifsn = 3, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + [IEEE80211_AC_BK] = { + .queue_id = HIF_QUEUE_ID_BACKGROUND, + .aifsn = 7, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + }; + + if (wfx_api_older_than(wdev, 2, 0)) { + default_edca_params[IEEE80211_AC_BE].queue_id = HIF_QUEUE_ID_BACKGROUND; + default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT; + } for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { if (!wdev->vif[i]) { @@ -28,12 +140,29 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) wvif->vif = vif; wvif->wdev = wdev; + INIT_WORK(&wvif->link_id_work, wfx_link_id_work); + INIT_DELAYED_WORK(&wvif->link_id_gc_work, wfx_link_id_gc_work); + + spin_lock_init(&wvif->ps_state_lock); + + INIT_WORK(&wvif->mcast_start_work, wfx_mcast_start_work); + INIT_WORK(&wvif->mcast_stop_work, wfx_mcast_stop_work); + timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); + BUG_ON(ARRAY_SIZE(default_edca_params) != ARRAY_SIZE(wvif->edca.params)); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + memcpy(&wvif->edca.params[i], &default_edca_params[i], sizeof(default_edca_params[i])); + tx_policy_init(wvif); return 0; } void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + + wfx_tx_queues_wait_empty_vif(wvif); + cancel_delayed_work_sync(&wvif->link_id_gc_work); + del_timer_sync(&wvif->mcast_timeout); } int wfx_start(struct ieee80211_hw *hw) @@ -43,4 +172,10 @@ int wfx_start(struct ieee80211_hw *hw) void wfx_stop(struct ieee80211_hw *hw) { + struct wfx_dev *wdev = hw->priv; + + wfx_tx_lock_flush(wdev); + wfx_tx_queues_clear(wdev); + wfx_tx_unlock(wdev); + WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked"); } |