Commit 9bca45f3 authored by Jérôme Pouiller's avatar Jérôme Pouiller Committed by Greg Kroah-Hartman

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: default avatarJérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-17-Jerome.Pouiller@silabs.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f4a71ba8
......@@ -9,6 +9,9 @@ wfx-y := \
fwio.o \
hif_tx.o \
hif_rx.o \
queue.o \
data_tx.o \
sta.o \
main.o \
sta.o \
debug.o
......
......@@ -220,6 +220,8 @@ static int bh_work_tx(struct wfx_dev *wdev, int max_msg)
if (try_wait_for_completion(&wdev->hif_cmd.ready)) {
WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
hif = wdev->hif_cmd.buf_send;
} else {
hif = wfx_tx_queues_get(wdev);
}
}
if (!hif)
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Datapath implementation.
*
* Copyright (c) 2017-2019, Silicon Laboratories, Inc.
* Copyright (c) 2010, ST-Ericsson
*/
#ifndef WFX_DATA_TX_H
#define WFX_DATA_TX_H
#include <linux/list.h>
#include <net/mac80211.h>
#include "hif_api_cmd.h"
#include "hif_api_mib.h"
// FIXME: use IEEE80211_NUM_TIDS
#define WFX_MAX_TID 8
struct wfx_tx_priv;
struct wfx_dev;
struct wfx_vif;
enum wfx_link_status {
WFX_LINK_OFF,
WFX_LINK_RESERVE,
WFX_LINK_SOFT,
WFX_LINK_HARD,
};
struct wfx_link_entry {
unsigned long timestamp;
enum wfx_link_status status;
uint8_t mac[ETH_ALEN];
uint8_t old_mac[ETH_ALEN];
uint8_t buffered[WFX_MAX_TID];
struct sk_buff_head rx_queue;
};
struct tx_policy {
struct list_head link;
uint8_t rates[12];
uint8_t usage_count;
uint8_t uploaded;
};
struct tx_policy_cache {
struct tx_policy cache[HIF_MIB_NUM_TX_RATE_RETRY_POLICIES];
// FIXME: use a trees and drop hash from tx_policy
struct list_head used;
struct list_head free;
spinlock_t lock;
};
struct wfx_tx_priv {
ktime_t xmit_timestamp;
struct ieee80211_key_conf *hw_key;
uint8_t link_id;
uint8_t raw_link_id;
uint8_t tid;
} __packed;
void tx_policy_init(struct wfx_vif *wvif);
void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
struct sk_buff *skb);
void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg);
void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb);
int wfx_unmap_link(struct wfx_vif *wvif, int link_id);
void wfx_link_id_work(struct work_struct *work);
void wfx_link_id_gc_work(struct work_struct *work);
int wfx_find_link_id(struct wfx_vif *wvif, const u8 *mac);
static inline struct wfx_tx_priv *wfx_skb_tx_priv(struct sk_buff *skb)
{
struct ieee80211_tx_info *tx_info;
if (!skb)
return NULL;
tx_info = IEEE80211_SKB_CB(skb);
return (struct wfx_tx_priv *) tx_info->rate_driver_data;
}
static inline struct hif_req_tx *wfx_skb_txreq(struct sk_buff *skb)
{
struct hif_msg *hif = (struct hif_msg *) skb->data;
struct hif_req_tx *req = (struct hif_req_tx *) hif->body;
return req;
}
#endif /* WFX_DATA_TX_H */
......@@ -53,6 +53,39 @@ static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *
return status;
}
static int hif_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_cnf_tx *body = buf;
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
WARN_ON(!wvif);
if (!wvif)
return -EFAULT;
wfx_tx_confirm_cb(wvif, body);
return 0;
}
static int hif_multi_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_cnf_multi_transmit *body = buf;
struct hif_cnf_tx *buf_loc = (struct hif_cnf_tx *) &body->tx_conf_payload;
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
int count = body->num_tx_confs;
int i;
WARN(count <= 0, "corrupted message");
WARN_ON(!wvif);
if (!wvif)
return -EFAULT;
for (i = 0; i < count; ++i) {
wfx_tx_confirm_cb(wvif, buf_loc);
buf_loc++;
}
return 0;
}
static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_ind_startup *body = buf;
......@@ -174,6 +207,10 @@ static const struct {
int msg_id;
int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf);
} hif_handlers[] = {
/* Confirmations */
{ HIF_CNF_ID_TX, hif_tx_confirm },
{ HIF_CNF_ID_MULTI_TRANSMIT, hif_multi_tx_confirm },
/* Indications */
{ HIF_IND_ID_STARTUP, hif_startup_indication },
{ HIF_IND_ID_WAKEUP, hif_wakeup_indication },
{ HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication },
......
......@@ -88,6 +88,7 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz
}
if (!ret) {
dev_err(wdev->dev, "chip did not answer\n");
wfx_pending_dump_old_frames(wdev, 3000);
wdev->chip_frozen = 1;
reinit_completion(&wdev->hif_cmd.done);
ret = -ETIMEDOUT;
......
......@@ -28,6 +28,7 @@
#include "bh.h"
#include "sta.h"
#include "debug.h"
#include "data_tx.h"
#include "secure_link.h"
#include "hif_tx_mib.h"
#include "hif_api_cmd.h"
......@@ -53,6 +54,7 @@ static const struct ieee80211_ops wfx_ops = {
.stop = wfx_stop,
.add_interface = wfx_add_interface,
.remove_interface = wfx_remove_interface,
.tx = wfx_tx,
};
bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor)
......@@ -215,6 +217,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
mutex_init(&wdev->rx_stats_lock);
init_completion(&wdev->firmware_ready);
wfx_init_hif_cmd(&wdev->hif_cmd);
wfx_tx_queues_init(wdev);
return wdev;
}
......@@ -222,6 +225,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
void wfx_free_common(struct wfx_dev *wdev)
{
mutex_destroy(&wdev->rx_stats_lock);
wfx_tx_queues_deinit(wdev);
ieee80211_free_hw(wdev->hw);
}
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* O(1) TX queue with built-in allocator.
*
* Copyright (c) 2017-2018, Silicon Laboratories, Inc.
* Copyright (c) 2010, ST-Ericsson
*/
#ifndef WFX_QUEUE_H
#define WFX_QUEUE_H
#include <linux/skbuff.h>
#include "hif_api_cmd.h"
#define WFX_MAX_STA_IN_AP_MODE 14
#define WFX_LINK_ID_AFTER_DTIM (WFX_MAX_STA_IN_AP_MODE + 1)
#define WFX_LINK_ID_UAPSD (WFX_MAX_STA_IN_AP_MODE + 2)
#define WFX_LINK_ID_MAX (WFX_MAX_STA_IN_AP_MODE + 3)
struct wfx_dev;
struct wfx_vif;
struct wfx_queue {
struct sk_buff_head queue;
int tx_locked_cnt;
int link_map_cache[WFX_LINK_ID_MAX];
u8 queue_id;
};
struct wfx_queue_stats {
int link_map_cache[WFX_LINK_ID_MAX];
struct sk_buff_head pending;
wait_queue_head_t wait_link_id_empty;
};
void wfx_tx_lock(struct wfx_dev *wdev);
void wfx_tx_unlock(struct wfx_dev *wdev);
void wfx_tx_flush(struct wfx_dev *wdev);
void wfx_tx_lock_flush(struct wfx_dev *wdev);
void wfx_tx_queues_init(struct wfx_dev *wdev);
void wfx_tx_queues_deinit(struct wfx_dev *wdev);
void wfx_tx_queues_lock(struct wfx_dev *wdev);
void wfx_tx_queues_unlock(struct wfx_dev *wdev);
void wfx_tx_queues_clear(struct wfx_dev *wdev);
bool wfx_tx_queues_is_empty(struct wfx_dev *wdev);
void wfx_tx_queues_wait_empty_vif(struct wfx_vif *wvif);
struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev);
void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff *skb);
size_t wfx_tx_queue_get_num_queued(struct wfx_queue *queue, u32 link_id_map);
struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id);
int wfx_pending_remove(struct wfx_dev *wdev, struct sk_buff *skb);
int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb);
unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb);
void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms);
#endif /* WFX_QUEUE_H */
......@@ -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");
}
......@@ -10,6 +10,14 @@
#include <net/mac80211.h>
#include "hif_api_cmd.h"
struct wfx_edca_params {
/* NOTE: index is a linux queue id. */
struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS];
bool uapsd_enable[IEEE80211_NUM_ACS];
};
struct wfx_sta_priv {
int link_id;
int vif_id;
......
......@@ -12,6 +12,7 @@
#define _WFX_TRACE_H
#include <linux/tracepoint.h>
#include <net/mac80211.h>
#include "bus.h"
#include "hif_api_cmd.h"
......@@ -349,6 +350,79 @@ TRACE_EVENT(bh_stats,
);
#define _trace_bh_stats(ind, req, cnf, busy, release) trace_bh_stats(ind, req, cnf, busy, release)
TRACE_EVENT(tx_stats,
TP_PROTO(struct hif_cnf_tx *tx_cnf, struct sk_buff *skb, int delay),
TP_ARGS(tx_cnf, skb, delay),
TP_STRUCT__entry(
__field(int, pkt_id)
__field(int, delay_media)
__field(int, delay_queue)
__field(int, delay_fw)
__field(int, ack_failures)
__field(int, flags)
__array(int, rate, 4)
__array(int, tx_count, 4)
),
TP_fast_assign(
// Keep sync with wfx_rates definition in main.c
static const int hw_rate[] = { 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13 };
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rates = tx_info->driver_rates;
int i;
__entry->pkt_id = tx_cnf->packet_id;
__entry->delay_media = tx_cnf->media_delay;
__entry->delay_queue = tx_cnf->tx_queue_delay;
__entry->delay_fw = delay;
__entry->ack_failures = tx_cnf->ack_failures;
if (!tx_cnf->status || __entry->ack_failures)
__entry->ack_failures += 1;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (rates[0].flags & IEEE80211_TX_RC_MCS)
__entry->rate[i] = rates[i].idx;
else
__entry->rate[i] = hw_rate[rates[i].idx];
__entry->tx_count[i] = rates[i].count;
}
__entry->flags = 0;
if (rates[0].flags & IEEE80211_TX_RC_MCS)
__entry->flags |= 0x01;
if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
__entry->flags |= 0x02;
if (rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD)
__entry->flags |= 0x04;
if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
__entry->flags |= 0x08;
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
__entry->flags |= 0x10;
if (tx_cnf->status)
__entry->flags |= 0x20;
if (tx_cnf->status == HIF_REQUEUE)
__entry->flags |= 0x40;
),
TP_printk("packet ID: %08x, rate policy: %s %d|%d %d|%d %d|%d %d|%d -> %d attempt, Delays media/queue/total: %4dus/%4dus/%4dus",
__entry->pkt_id,
__print_flags(__entry->flags, NULL,
{ 0x01, "M" }, { 0x02, "S" }, { 0x04, "G" },
{ 0x08, "R" }, { 0x10, "D" }, { 0x20, "F" },
{ 0x40, "Q" }),
__entry->rate[0],
__entry->tx_count[0],
__entry->rate[1],
__entry->tx_count[1],
__entry->rate[2],
__entry->tx_count[2],
__entry->rate[3],
__entry->tx_count[3],
__entry->ack_failures,
__entry->delay_media,
__entry->delay_queue,
__entry->delay_fw
)
);
#define _trace_tx_stats(tx_cnf, skb, delay) trace_tx_stats(tx_cnf, skb, delay)
#endif
/* This part must be outside protection */
......
......@@ -14,8 +14,11 @@
#include <net/mac80211.h>
#include "bh.h"
#include "data_tx.h"
#include "main.h"
#include "queue.h"
#include "secure_link.h"
#include "sta.h"
#include "hif_tx.h"
#include "hif_api_general.h"
......@@ -38,6 +41,10 @@ struct wfx_dev {
int chip_frozen;
struct wfx_hif_cmd hif_cmd;
struct wfx_queue tx_queue[4];
struct wfx_queue_stats tx_queue_stats;
int tx_burst_idx;
atomic_t tx_lock;
struct hif_rx_stats rx_stats;
struct mutex rx_stats_lock;
......@@ -47,6 +54,28 @@ struct wfx_vif {
struct wfx_dev *wdev;
struct ieee80211_vif *vif;
int id;
u32 link_id_map;
struct wfx_link_entry link_id_db[WFX_MAX_STA_IN_AP_MODE];
struct delayed_work link_id_gc_work;
struct work_struct link_id_work;
bool aid0_bit_set;
bool mcast_tx;
bool mcast_buffered;
struct timer_list mcast_timeout;
struct work_struct mcast_start_work;
struct work_struct mcast_stop_work;
struct tx_policy_cache tx_policy_cache;
struct work_struct tx_policy_upload_work;
u32 sta_asleep_mask;
u32 pspoll_mask;
spinlock_t ps_state_lock;
struct wfx_edca_params edca;
};
static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id)
......@@ -62,4 +91,33 @@ static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id)
return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv;
}
static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev, struct wfx_vif *cur)
{
int i;
int mark = 0;
struct wfx_vif *tmp;
if (!cur)
mark = 1;
for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) {
tmp = wdev_to_wvif(wdev, i);
if (mark && tmp)
return tmp;
if (tmp == cur)
mark = 1;
}
return NULL;
}
static inline int memzcmp(void *src, unsigned int size)
{
uint8_t *buf = src;
if (!size)
return 0;
if (*buf)
return 1;
return memcmp(buf, buf + 1, size - 1);
}
#endif /* WFX_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment